js高级

js高级

面向对象

面向对象的思维特点:

  • 抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)
  • 对类进行实例化, 获取类的对象

面向对象编程我们考虑的是有哪些对象,按照面向对象的思维特点,不断的创建对象,使用对象,指挥对象做事情.

对象

定义: 在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。

  • 对象是由属性和方法组成的:
    • 属性:事物的特征,在对象中用属性来表示(常用名词)
    • 方法:事物的行为,在对象中用方法来表示(常用动词)

类 class

定义:

  • 类抽象了对象的公共部分,它泛指某一大类(class)
  • 对象特指某一个,通过类实例化一个具体的对象

创建类

class name {            //创建
  // class body
}      

var xx = new name();     //实例化


类 constructor 构造函数

constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过 new 命令生成对象实例时,自动调用该方法。如果没有显示定义, 类内部会自动给我们创建一个constructor()


类添加方法

注意:方法之间不能加逗号分隔,同时方法不需要添加 function 关键字。


类的继承

子类可以继承父类的一些属性和方法。

class Father {
    constructor() {

    }
    money() {
        console.log(100);

    }
}
class Son extends Father {

}
var son = new Son();
son.money();     //==>100

super

super 关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数

super 关键字调用父类的构造函数


class Father {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    sum() {
        console.log(this.x + this.y);

    }
}

class Son extends Father {
    constructor(x, y) {
        super(x, y); //super 关键字调用了父类中的构造函数
    }
}
var son = new Son(1, 2);
var son1 = new Son(11, 22);
son.sum();  //==> 3
son1.sum(); //==> 33

总结:
类中的方法使用的是 constructor 里面的数据,所以子类要使用父类的方法,需要把值传给父类,否则this的指向不一致,会报错

super 关键字调用父类的普通函数


注意: 子类在构造函数中使用super, 必须放到 this 前面 (必须先调用父类的构造方法,在使用子类构造方法)

      

三个注意点:

  • 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象.
  • 类里面的共有属性和方法一定要加this使用.
  • 类里面的this指向问题.
  • constructor 里面的this指向实例对象, 方法里面的this 指向这个方法的调用者
    
    

构造函数

构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。


  • new 在执行时会做四件事情:
    • 在内存中创建一个新的空对象。
    • 让 this 指向这个新的对象。
    • 执行构造函数里面的代码,给这个新对象添加属性和方法。
    • 返回这个新对象(所以构造函数里面不需要 return )。

实例成员和静态成员


构造函数原型 prototype

JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。注意这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。


对象原型 proto

  • 对象都会有一个属性 proto 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 proto 原型的存在。
    • __proto__对象原型和原型对象 prototype 是等价的
    • __proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype

constructor 构造函数

对象原型( proto)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。
constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。

    

构造函数、实例、原型对象三者之间的关系

js高级_第1张图片

对象实例能指回构造函数是通过原型对象的 constructor 指回去的

原型链

js高级_第2张图片


JavaScript 的成员查找机制(规则)

  • 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
  • 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。
  • 如果还没有就查找原型对象的原型(Object的原型对象)。
  • 依此类推一直找到 Object 为止(null)。
  • __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。

原型对象this指向

构造函数中的this 指向我们实例对象.
原型对象里面放的是方法, 这个方法里面的this 指向的是 这个方法的调用者, 也就是这个实例对象.


扩展内置对象

可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。


注: 数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {} ,只能是 Array.prototype.xxx = function(){} 的方式

call() 方法

调用这个函数, 并且修改函数运行时的 this 指向

使用: fun.call(thisArg, arg1, arg2, ...)

  • thisArg :当前调用函数 this 的指向对象
  • arg1,arg2:传递的其他参数

ES6之前并没有给我们提供 extends 继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。

借用构造函数继承父类型属性

核心原理: 通过 call() 把父类型的 this 指向子类型的 this ,这样就可以实现子类型继承父类型的属性。


借用原型对象继承父类型方法

一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法。

  • 核心原理:
    • 将子类所共享的方法提取出来,让子类的 prototype 原型对象 = new 父类()
    • 本质:子类原型对象等于是实例化父类,因为父类实例化之后另外开辟空间,就不会影响原来父类原型对象
    • 将子类的 constructor 从新指向子类的构造函数

类的本质

1. class本质还是function.
2. 类的所有方法都定义在类的prototype属性上
3. 类创建的实例,里面也有__proto__ 指向类的prototype原型对象
4. 所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
5. 所以ES6的类其实就是语法糖.
6. 语法糖:语法糖就是一种便捷写法.   简单理解, 有两种方法可以实现同样的功能, 但是一种写法更加清晰、方便,那么这个方法就是语法糖

数组方法

迭代(遍历)方法:forEach()、map()、filter()、some()、every();

forEach()

使用: array.forEach(function(currentValue, index, arr))

  • currentValue:数组当前项的值
  • index:数组当前项的索引
  • arr:数组对象本身
// forEach 迭代(遍历) 数组
var arr = [1, 2, 3];
var sum = 0;
arr.forEach(function(value, index, array) {
    console.log('每个数组元素' + value);
    console.log('每个数组元素的索引号' + index);
    console.log('数组本身' + array);
    sum += value;
})
console.log(sum);

filter()

使用: array.filter(function(currentValue, index, arr))

  • filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组
  • 注意它直接返回一个新数组
  • currentValue: 数组当前项的值
  • index:数组当前项的索引
  • arr:数组对象本身
// filter 筛选数组
var arr = [12, 66, 4, 88, 3, 7];
var newArr = arr.filter(function(value, index) {
    // return value >= 20;
    return value % 2 === 0;
});
console.log(newArr);

some()–查找唯一的元素用它,因为它找到这个元素,就不在进行循环,效率更高

使用: array.some(function(currentValue, index, arr))

  • some() 方法用于检测数组中的元素是否满足指定条件. 通俗点 查找数组中是否有满足条件的元素
  • 注意它返回值是布尔值, 如果查找到这个元素, 就返回true , 如果查找不到就返回false.
  • 如果找到第一个满足条件的元素,则终止循环. 不在继续查找.
  • currentValue: 数组当前项的值
  • index:数组当前项的索引
  • arr:数组对象本身

forEach() 和 some() 的区别


字符串方法

trim() 方法会从一个字符串的两端删除空白字符。

使用: str.trim()
trim() 方法并不影响原字符串本身,它返回的是一个新的字符串


     
    

对象方法

Object.keys() 用于获取对象自身所有的属性

使用: Object.keys(obj)

返回一个由属性名组成的数组

Object.defineProperty() 定义新属性或修改原有的属性

使用: Object.defineProperty(obj, prop, descriptor)

  • obj:必需。目标对象

  • prop:必需。需定义或修改的属性的名字

  • descriptor:必需。目标属性所拥有的特性

  • Object.defineProperty() 第三个参数 descriptor 说明: 以对象形式 { } 书写

    • value: 设置属性的值 默认为undefined
    • writable: 值是否可以重写。true | false 默认为false
    • enumerable: 目标属性是否可以被枚举。true | false 默认为 false
    • configurable: 目标属性是否可以被删除或是否可以再次修改特性 true | false 默认为false

函数的定义方式

  1. 函数声明方式 function 关键字 (命名函数)
  2. 函数表达式 (匿名函数)
  3. new Function()
    使用: var fn = new Function('参数1','参数2'..., '函数体')
  • Function 里面参数都必须是字符串格式
  • 第三种方式执行效率低,也不方便书写,因此较少使用
  • 所有函数都是 Function 的实例(对象)
  • 函数也属于对象

js高级_第3张图片

函数调用

  1. 普通函数
  2. 对象的方法
  3. 构造函数
  4. 绑定事件函数
  5. 定时器函数
  6. 立即执行函数

函数内 this 的指向

这些 this 的指向,是当我们调用函数的时候确定的。 调用方式的不同决定了this 的指向不同. 一般指向我们的调用者.

js高级_第4张图片


    
    

改变函数内部 this 指向

JavaScript 为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部 this 的指向问题,常用的有 bind()、call()、apply() 三种方法。

call 方法

call() 方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。

fun.call(thisArg, arg1, arg2, ...)

  • thisArg:在 fun 函数运行时指定的 this 值
  • arg1,arg2:传递的其他参数
  • 返回值就是函数的返回值,因为它就是调用函数
  • 因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承

apply 方法

apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。

使用: fun.apply(thisArg, [argsArray])

  • thisArg:在fun函数运行时指定的 this 值
  • argsArray:传递的值,必须包含在数组里面
  • 返回值就是函数的返回值,因为它就是调用函数
  • 因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值

bind 方法

bind() 方法不会调用函数。但是能改变函数内部this 指向

使用:fun.bind(thisArg, arg1, arg2, ...)

  • thisArg:在 fun 函数运行时指定的 this 值
  • arg1,arg2:传递的其他参数
  • 返回由指定的 this 值和初始化参数改造的原函数拷贝
  • 因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind

    
    
    
    


call apply bind 总结

  • 相同点: 都可以改变函数内部的this指向.

  • 区别点:

    • call 和 apply 会调用函数, 并且改变函数内部this指向.
    • call 和 apply 传递的参数不一样, call 传递参数 aru1, aru2…形式 apply 必须数组形式[arg]
    • bind 不会调用函数, 可以改变函数内部this指向.
  • 主要应用场景:

    • call 经常做继承.
    • apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
    • bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.

严格模式

严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。

  • 严格模式对正常的 JavaScript 语义做了一些更改:
    • 消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为。
    • 消除代码运行的一些不安全之处,保证代码运行的安全。
    • 提高编译器效率,增加运行速度。
    • 禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫。比如一些保留字如:class, enum, export, extends, import, super 不能做变量名

开启严格模式

为脚本开启严格模式




为函数开启严格模式



严格模式中的变化

  • 变量规定
    • 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用var 命令声明,然后再使用。
    • 严禁删除已经声明变量。例如,delete x; 语法是错误的。
  • 严格模式下 this 指向问题
    • 以前在全局作用域函数中的 this 指向 window 对象。
    • 严格模式下全局作用域中函数中的 this 是 undefined。
    • 以前构造函数时不加 new也可以 调用,当普通函数,this 指向全局对象
    • 严格模式下,如果 构造函数不加new调用, this 指向的是undefined 如果给他赋值则 会报错
    • new 实例化的构造函数指向创建的对象实例。
    • 定时器 this 还是指向 window 。
    • 事件、对象还是指向调用者。
  • 函数变化
    • 函数不能有重名的参数。
    • 函数必须声明在顶层.新版本的 JavaScript 会引入“块级作用域”( ES6 中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数。
    

高阶函数

高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。




  • 此时fn 就是一个高阶函数
  • 函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。 最典型的就是作为回调函数。
  • 同理,函数也可以作为返回值传递回来

闭包—延伸变量的作用范围。

合理的使用闭包


    

闭包应用-点击li输出当前li的索引号


    
    

立即执行函数中的i在立即执行函数执行完之后应该要销毁的,但是内部的点击事件需要使用i,所以要等到点击事件结束后才能销毁,如果一直不点击,i一直没销毁,会存在内存泄漏的问题

闭包应用-3秒钟之后,打印所有li元素的内容


    
    

闭包应用-计算打车价格

    

闭包总结

  1. 闭包是什么?

    • 闭包是一个函数 (一个作用域可以访问另外一个函数的局部变量)
  2. 闭包的作用是什么?

    • 延伸变量的作用范围

递归

什么是递归?

  • 如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。
  • 简单理解:函数内部自己调用自己(重复、多次执行某段代码,跟循环类似), 这个函数就是递归函数
  • 递归函数的作用和循环效果一样
  • 由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return。

利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * …n

    

输入id号,就可以返回相应的数据对象

    

浅拷贝

浅拷贝只是拷贝一层, 更深层次对象级别的只拷贝引用(即地址).

js高级_第5张图片

Object.assign(target, …sources) es6 新增方法可以实现浅拷贝


浅拷贝中复杂类型数据被修改后,原数据中也会被修改(因为地址相同)

深拷贝

深拷贝拷贝多层, 每一级别的数据都会拷贝.
js高级_第6张图片


ES6

let 用于声明变量的关键字。


js高级_第7张图片

js高级_第8张图片

	

const 声明常量,常量就是值(内存地址)不能变化的量。

  • 具有块级作用域
  • 声明常量时必须赋值
  • 常量赋值后,值不能修改。
	

let、const、var 的区别

js高级_第9张图片

什么时候使用?

  • 存储的数据不需要变化,尽量使用 const 关键字,例如:函数定义,π值,或是数学公式中恒定不变的值—const 关键字定义的常量js解析时不会实时监控它,所以const 比 let 效率高

解构赋值(分解数据结构,为变量赋值)

按照一定模式,从数组中或对象中提取值,将提取出来的值赋值给另外的变量。

数组解构


对象解构


箭头函数


面试题

.

剩余参数

剩余参数语法允许我们将一个不定数量的参数表示为一个数组。

 function sum (first, ...args) {
     console.log(first); // 10
     console.log(args); // [20, 30] 
 }
 sum(10, 20, 30)

剩余参数和解构配合使用

 let students = ['wangwu', 'zhangsan', 'lisi'];
 let [s1, ...s2] = students; 
 console.log(s1);  // 'wangwu' 
 console.log(s2);  // ['zhangsan', 'lisi']


Array 的扩展方法

扩展运算符(展开语法)


	
1
4
3
6
2
5

构造函数方法:Array.from()

将伪数组或可遍历对象转换为真正的数组

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
}; 
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

let arrayLike = { 
     "0": 1,
     "1": 2,
     "length": 2
 }
 let newAry = Array.from(aryLike, item => item *2)// [2, 4]

实例方法:find()

用于找出第一个符合条件的数组成员,如果没有找到返回undefined

 let ary = [{
     id: 1,
     name: '张三‘
 }, { 
     id: 2,
     name: '李四‘
 }]; 
 let target = ary.find((item, index) => item.id == 2);

实例方法:findIndex()

用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1

let ary = [1, 5, 10, 15];
let index = ary.findIndex((value, index) => value > 9); 
console.log(index); // 2

实例方法:includes()

表示某个数组是否包含给定的值,返回布尔值。

[1, 2, 3].includes(2) // true 
[1, 2, 3].includes(4) // false

String 的扩展方法

模板字符串

ES6新增的创建字符串的方式,使用反引号定义。

let name = zhangsan;

模板字符串中可以解析变量—${变量名}。

let name = '张三'; 
let sayHello = `hello,my name is ${name}`; // hello, my name is zhangsan

模板字符串中可以换行

 let result = { 
     name: 'zhangsan', 
     age: 20, 
     sex: '男' 
 } 
 let html = ` 
${result.name} ${result.age} ${result.sex}
`;

在模板字符串中可以调用函数,调用位置显示函数执行后的返回值。

const fn = () => {
    return '我是fn函数'
}

let html = `我是模板字符串 ${fn()}`;
console.log(html)

实例方法:startsWith() 和 endsWith()

  • startsWith():表示参数字符串是否在原字符串的头部,返回布尔值
  • endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值

实例方法:repeat()

repeat方法表示将原字符串重复n次,返回一个新字符串。

'x'.repeat(3)      // "xxx" 
'hello'.repeat(2)  // "hellohello"

Set 数据结构

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set本身是一个构造函数,用来生成 Set 数据结构。
const s = new Set();

Set函数可以接受一个数组作为参数,用来初始化。
const set = new Set([1, 2, 3, 4, 4]);

  • add(value):添加某个值,返回 Set 结构本身
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功
  • has(value):返回一个布尔值,表示该值是否为 Set 的成员
  • clear():清除所有成员,没有返回值

遍历:

Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。

const s5 = new Set(['a', 'b', 'c']);
s5.forEach(value => {
    console.log(value)
})

ary = [{
id: 1,
name: '张三‘
}, {
id: 2,
name: '李四‘
}];
let target = ary.find((item, index) => item.id == 2);


### 实例方法:findIndex()

用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1

let ary = [1, 5, 10, 15];
let index = ary.findIndex((value, index) => value > 9);
console.log(index); // 2


### 实例方法:includes()
 
表示某个数组是否包含给定的值,返回布尔值。

[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false

## String 的扩展方法

### 模板字符串

ES6新增的创建字符串的方式,使用反引号定义。

let name = `zhangsan`;

模板字符串中可以解析变量---${变量名}。

let name = ‘张三’;
let sayHello = hello,my name is ${name}; // hello, my name is zhangsan


模板字符串中可以换行

let result = {
name: ‘zhangsan’,
age: 20,
sex: ‘男’
}
let html = `


r e s u l t . n a m e < / s p a n > < s p a n > {result.name} result.name</span><span>{result.age}
${result.sex}

`;

在模板字符串中可以调用函数,调用位置显示函数执行后的返回值。

const fn = () => {
return ‘我是fn函数’
}

let html = 我是模板字符串 ${fn()};
console.log(html)


### 实例方法:startsWith() 和 endsWith()

+ startsWith():表示参数字符串是否在原字符串的头部,返回布尔值
+ endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值


### 实例方法:repeat()

repeat方法表示将原字符串重复n次,返回一个新字符串。

‘x’.repeat(3) // “xxx”
‘hello’.repeat(2) // “hellohello”

## Set 数据结构

ES6 提供了新的数据结构  Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set本身是一个构造函数,用来生成  Set  数据结构。
`const s = new Set();`

Set函数可以接受一个数组作为参数,用来初始化。
`const set = new Set([1, 2, 3, 4, 4]);`

+ add(value):添加某个值,返回 Set 结构本身
+ delete(value):删除某个值,返回一个布尔值,表示删除是否成功
+ has(value):返回一个布尔值,表示该值是否为 Set 的成员
+ clear():清除所有成员,没有返回值

遍历:

Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。

const s5 = new Set([‘a’, ‘b’, ‘c’]);
s5.forEach(value => {
console.log(value)
})

你可能感兴趣的:(笔记)