解构赋值
应用:
- 交换变量值:
[x, y] = [y, x]
- 返回函数多个值:
const [x, y, z] = Func()
- 定义函数参数:
Func([1, 2])
- 提取JSON数据:
const { name, version } = packageJson
- 定义函数参数默认值:
function Func({ x = 1, y = 2 } = {}) {}
- 遍历Map结构:
for (let [k, v] of Map) {}
- 输入模块指定属性和方法:
const { readFile, writeFile } = require("fs")
字符串扩展
- Unicode表示法:大括号包含表示Unicode字符(\u{0xXX}或\u{0XXX})
- 字符串遍历:可通过for-of遍历字符串
1
2
3for(let a of 'abc'){
console.log(a);
} - 字符串模板:可单行可多行可插入变量的增强版字符串
String.raw()
:返回把字符串所有变量替换且对斜杠进行转义的结果String.fromCodePoint()
:返回码点对应字符codePointAt()
:返回字符对应码点(String.fromCodePoint()的逆操作)repeat()
:把字符串重复n次,返回新字符串matchAll()
:返回正则表达式在字符串的所有匹配includes()
:是否存在指定字符串startsWith()
:是否存在字符串头部指定字符串endsWith()
:是否存在字符串尾部指定字符串- *以上方法均用于4个字节存储的Unicode字符上**
数值扩展
- 二进制表示法:0b或0B开头表示二进制(0bXX或0BXX)
- 八进制表示法:0o或0O开头表示二进制(0oXX或0OXX)
Number.parseInt()
:返回转换值的整数部分Number.parseFloat()
:返回转换值的浮点数部分Number.isFinite()
:是否为有限数值Number.isNaN()
:是否为NaNNumber.isInteger()
:是否为整数Math.trunc()
:返回数值整数部分Math.sign()
:返回数值类型(正数1、负数-1、零0)
对象扩展
- 简洁表示法:直接写入变量和函数作为对象的属性和方法
1
2
3
4{
prop,
method() {}
} - 属性名表达式:字面量定义对象时使用
[]
定义键1
2
3
4
5
6
7// 不能与上面的特性同时使用
let foo = 'bar';
{
[foo]: 'foo',
// 这样是不行的
[foo]
} - 方法的name属性:返回方法函数名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29// 如果是get和set则不能直接取到name,需要通过描述符来访问
const obj = {
get foo() {},
set foo(x) {}
};
obj.foo.name
// TypeError: Cannot read property 'name' of undefined
const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');
// 这个方法会返回一个对象里面的内容如下
/**
{
value: 1 // descriptor没有这个值,以get代替
writable: true // descriptor没有这个值,以set代替
enumerable: true
configurable: true
}
*/
descriptor.get.name // "get foo"
descriptor.set.name // "set foo"
// 通过new Function创建的函数
(new Function()).name // "anonymous"
// bind函数返回的函数
var doSomething = function() {
// ...
};
doSomething.bind().name // "bound doSomething" - 属性的可枚举性和遍历:描述对象的enumerable
- super关键字:指向当前对象的原型对象(只能用在对象的简写方法中method() {})
Object.is()
:对比两值是否相等Object.assign()
:合并对象(浅拷贝),返回原对象Object.getPrototypeOf()
:返回对象的原型对象Object.setPrototypeOf()
:设置对象的原型对象__proto__
:返回或设置对象的原型对象
属性遍历
- 描述:自身、可继承、可枚举、非枚举、Symbol
- 遍历
for-in
:遍历对象自身可继承可枚举属性Object.keys()
:返回对象自身可枚举属性的键组成的数组Object.getOwnPropertyNames()
:返回对象自身可继承可枚举非枚举属性的键组成的数组Object.getOwnPropertySymbols()
:返回对象Symbol属性的键组成的数组Reflect.ownKeys()
:返回对象自身可继承可枚举非枚举Symbol属性的键组成的数组 - 规则
首先遍历所有数值键,按照数值升序排列
其次遍历所有字符串键,按照加入时间升序排列
最后遍历所有Symbol键,按照加入时间升序排列
数组扩展
扩展运算符(…):转换数组为用逗号分隔的参数序列(
[...arr]
,相当于rest/spread参数的逆运算)Array.from()
:转换具有Iterator接口的数据结构为真正数组,返回新数组类数组对象:包含length的对象、Arguments对象、NodeList对象
可遍历对象:String、Set结构、Map结构、Generator函数
Array.of()
:转换一组值为真正数组,返回新数组copyWithin()
:把指定位置的成员复制到其他位置,返回原数组find()
:返回第一个符合条件的成员findIndex()
:返回第一个符合条件的成员索引值fill()
:根据指定值填充整个数组,返回原数组keys()
:返回以索引值为遍历器的对象values()
:返回以属性值为遍历器的对象entries()
:返回以索引值和属性值为遍历器的对象,[0, 1]数组空位:ES6明确将数组空位转为undefined(空位处理规不一,建议避免出现,chrome使用empty处理)
扩展应用
- 克隆数组:
const arr = [...arr1](浅克隆)
- 合并数组:
const arr = [...arr1, ...arr2]
- 拼接数组:
arr.push(...arr1)
- 代替apply:
Math.max.apply(null, [x, y]) => Math.max(...[x, y])
- 转换字符串为数组:
[..."hello"]
- 转换类数组对象为数组:
[...Arguments, ...NodeList]
- 转换可遍历对象为数组:
[...String, ...Set, ...Map, ...Generator]
- 与数组解构赋值结合:
const [x, ...rest/spread] = [1, 2, 3]
- 计算Unicode字符长度:
Array.from("hello").length => [..."hello"].length
重点难点
- 使用
keys()、values()、entries()
返回的遍历器对象,可用for-of
自动遍历或next()
手动遍历 - 扩展运算符和Array.from()区别
扩展运算符调用的是Symbol.iterator接口,Array.from对于具有length属性的数据结构即可1
2
3let obj = {length: 3};
Array.from(obj);// 可以转换
[...obj]// 会报错
函数扩展
- 参数默认值:为函数参数指定默认值
- 形式:function Func(x = 1, y = 2) {}
- 参数赋值:惰性求值(函数调用后才求值)
- 参数位置:尾参数
- 参数作用域:函数作用域
- 声明方式:默认声明,不能用const或let再次声明
- length:返回没有指定默认值的参数个数
- 与解构赋值默认值结合:function Func({ x = 1, y = 2 } = {}) {}
- 应用
指定某个参数不得省略,省略即抛出错误:function Func(x = throwMissing()) {}
将参数默认值设为undefined,表明此参数可省略:Func(undefined, 1)
- rest/spread参数(…):返回函数多余参数
形式:以数组的形式存在,之后不能再有其他参数
作用:代替Arguments对象
length:返回没有指定默认值的参数个数但不包括rest/spread参数 - 严格模式:在严格条件下运行JS
应用:只要函数参数使用默认值、解构赋值、扩展运算符,那么函数内部就不能显式设定为严格模式 - name属性:返回函数的函数名
- 将匿名函数赋值给变量:空字符串(ES5)、变量名(ES6)
- 将具名函数赋值给变量:函数名(ES5和ES6)
- bind返回的函数:bound 函数名(ES5和ES6)
- Function构造函数返回的函数实例:anonymous(ES5和ES6)
- 箭头函数(=>):函数简写
- 无参数:
() => {}
- 单个参数:
x => {}
- 多个参数:
(x, y) => {}
- 解构参数:
({x, y}) => {}
- 嵌套使用:部署管道机制
1
2
3
4
5
6
7
8
9// 返回一个函数接受val参数,作为数组的reduce的第一个参数,也就是a,然后在调用传入的第一个参数的时候就会将a作为参数
const pipeline = (...funcs) =>
val => funcs.reduce((a, b) => b(a), val);
const plus1 = a => a + 1;
const mult2 = a => a * 2;
const addThenMult = pipeline(plus1, mult2);
addThenMult(5) - 注意点
- this指向固定化
并非因为内部有绑定this的机制,而是根本没有自己的this,导致内部的this就是外层代码块的this
因为没有this,因此不能用作构造函数 - 不能使用arguments对象
- 不能使用yield命令,不能做Generator函数
- this指向固定化
- 无参数:
- 尾调用优化:只保留内层函数的调用帧(暂时不了解)
- 详情见尾调用优化
- 尾调用
- 定义:某个函数的最后一步是调用另一个函数
- 形式:
function f(x) { return g(x); }
- 尾递归
- 定义:函数尾调用自身
- 作用:只要使用尾递归就不会发生栈溢出,相对节省内存
- 实现:把所有用到的内部变量改写成函数的参数并使用参数默认值
箭头函数误区
- 函数体内的this是定义时所在的对象而不是使用时所在的对象
- 可让this指向固定化,这种特性很有利于封装回调函数
- 不可当作构造函数,因此箭头函数不可使用new命令
- 不可使用yield命令,因此箭头函数不能用作Generator函数
- 不可使用Arguments对象,此对象在函数体内不存在(可用rest/spread参数代替)
- 不适用场合
- 定义对象方法对象不会构成单独的作用域,所以this会被绑定到全局上
1
2
3
4
5
6const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
} - 动态this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
```返回对象时必须在对象外面加上括号
- - -
# Symbol
* 定义:独一无二的值
* 声明:`const set = Symbol(str)`
* 入参:字符串(可选)
* 方法
* `Symbol()`:创建以参数作为描述的Symbol值(不登记在全局环境)
* `Symbol.for()`:创建以参数作为描述的Symbol值,如存在此参数则返回原有的Symbol值(先搜索后创建,登记在全局环境)
* `Symbol.keyFor()`:返回已登记的Symbol值的描述(只能返回Symbol.for()的key)
* `Object.getOwnPropertySymbols()`:返回对象中所有用作属性名的Symbol值的数组
* 内置
* `Symbol.hasInstance`:指向一个内部方法,当其他对象使用instanceof运算符判断是否为此对象的实例时会调用此方法
* `Symbol.isConcatSpreadable`:指向一个布尔,定义对象用于Array.prototype.concat()时是否可展开
* `Symbol.species`:指向一个构造函数,当实例对象使用自身构造函数时会调用指定的构造函数
* `Symbol.match`:指向一个函数,当实例对象被String.prototype.match()调用时会重新定义match()的行为
* `Symbol.replace`:指向一个函数,当实例对象被String.prototype.replace()调用时会重新定义replace()的行为
* `Symbol.search`:指向一个函数,当实例对象被String.prototype.search()调用时会重新定义search()的行为
* `Symbol.split`:指向一个函数,当实例对象被String.prototype.split()调用时会重新定义split()的行为
* `Symbol.iterator`:指向一个默认遍历器方法,当实例对象执行for-of时会调用指定的默认遍历器
* `Symbol.toPrimitive`:指向一个函数,当实例对象被转为原始类型的值时会返回此对象对应的原始类型值
* `Symbol.toStringTag`:指向一个函数,当实例对象被Object.prototype.toString()调用时其返回值会出现在toString()返回的字符串之中表示对象的类型
* `Symbol.unscopables`:指向一个对象,指定使用with时哪些属性会被with环境排除
## 数据类型
* Undefined
* Null
* String
* Number
* Boolean
* Object(包含Array、Function、Date、RegExp、Error)
* Symbol
- - -
# Set
* 定义:类似于数组的数据结构,成员值都是唯一且没有重复的值
* 声明:`const set = new Set(arr)`
* 入参:具有Iterator接口的数据结构
* 属性
* constructor:构造函数,返回Set
* size:返回实例成员总数
* 方法
* `add()`:添加值,返回实例
* `delete()`:删除值,返回布尔
* `has()`:检查值,返回布尔
* `clear()`:清除所有成员
* `keys()`:返回以属性值为遍历器的对象
* `values()`:返回以属性值为遍历器的对象
* `entries()`:返回以属性值和属性值为遍历器的对象
* `forEach()`:使用回调函数遍历每个成员
## 应用场景
* 去重字符串:`[...new Set(str)].join("")`
* 去重数组:`[...new Set(arr)]或Array.from(new Set(arr))`
* 集合数组
* 声明:`const a = new Set(arr1)、const b = new Set(arr2)`
* 并集:`new Set([...a, ...b])`
* 交集:`new Set([...a].filter(v => b.has(v)))`
* 差集:`new Set([...a].filter(v => !b.has(v)))`
* 映射集合
* 声明:`let set = new Set(arr)`
* 映射:`set = new Set([...set].map(v => v * 2))或set = new Set(Array.from(set, v => v * 2))`
## 重点难点
* 遍历顺序:插入顺序
* 没有键只有值,可认为键和值两值相等
* 添加多个NaN时,只会存在一个NaN
* 添加相同的对象时,会认为是不同的对象
* 添加值时不会发生类型转换(5 !== "5")
* keys()和values()的行为完全一致,entries()返回的遍历器同时包括键和值且两值相等
- - -
# Map
* 定义:类似于对象的数据结构,成员键可以是任何类型的值
* 声明:const set = new Map(arr)
* 入参:具有Iterator接口且每个成员都是一个双元素数组的数据结构
* 属性
* constructor:构造函数,返回Map
* size:返回实例成员总数
* 方法
* get():返回键值对
* set():添加键值对,返回实例
* delete():删除键值对,返回布尔
* has():检查键值对,返回布尔
* clear():清除所有成员
* keys():返回以键为遍历器的对象
* values():返回以值为遍历器的对象
* entries():返回以键和值为遍历器的对象
* forEach():使用回调函数遍历每个成员
* 重点难点
* 遍历顺序:插入顺序
* 对同一个键多次赋值,后面的值将覆盖前面的值
* 对同一个对象的引用,被视为一个键
* 对同样值的两个实例,被视为两个键
* 键跟内存地址绑定,只要内存地址不一样就视为两个键
* 添加多个以NaN作为键时,只会存在一个以NaN作为键的值
* Object结构提供字符串—值的对应,Map结构提供值—值的对应
- - -
# Proxy
* 定义:修改某些操作的默认行为
* 声明:`const proxy = new Proxy(target, handler)`
* 入参
* target:拦截的目标对象
* handler:定制拦截行为
* 方法
* `Proxy.revocable()`:返回可取消的Proxy实例(返回{ proxy, revoke },通过revoke()取消代理)
* 拦截方式
* `get()`:拦截对象属性读取
* `set()`:拦截对象属性设置,返回布尔
* `has()`:拦截对象属性检查k in obj,返回布尔
* `deleteProperty()`:拦截对象属性删除delete obj[k],返回布尔
* `defineProperty()`:拦截对象属性定义Object.defineProperty()、Object.defineProperties(),返回布尔
* `ownKeys()`:拦截对象属性遍历for-in、Object.keys()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols(),返回数组
* `getOwnPropertyDescriptor()`:拦截对象属性描述读取Object.getOwnPropertyDescriptor(),返回对象
* `getPrototypeOf()`:拦截对象原型读取instanceof、Object.getPrototypeOf()、Object.prototype.__proto__、Object.prototype.isPrototypeOf()、Reflect.getPrototypeOf(),返回对象
* `setPrototypeOf()`:拦截对象原型设置Object.setPrototypeOf(),返回布尔
* `isExtensible()`:拦截对象是否可扩展读取Object.isExtensible(),返回布尔
* `preventExtensions()`:拦截对象不可扩展设置Object.preventExtensions(),返回布尔
* `apply()`:拦截Proxy实例作为函数调用proxy()、proxy.apply()、proxy.call()
* `construct()`:拦截Proxy实例作为构造函数调用new proxy()
* 应用场景
* `Proxy.revocable()`:不允许直接访问对象,必须通过代理访问,一旦访问结束就收回代理权不允许再次访问
* `get()`:读取未知属性报错、读取数组负数索引的值、封装链式操作、生成DOM嵌套节点
* `set()`:数据绑定(Vue数据绑定实现原理)、确保属性值设置符合要求、防止内部属性被外部读写
* `has()`:隐藏内部属性不被发现、排除不符合属性条件的对象
* `deleteProperty()`:保护内部属性不被删除
* `defineProperty()`:阻止属性被外部定义
* `ownKeys()`:保护内部属性不被遍历
* 重点难点
* 要使Proxy起作用,必须针对实例进行操作,而不是针对目标对象进行操作
* 没有设置任何拦截时,等同于直接通向原对象
* 属性被定义为不可读写/扩展/配置/枚举时,使用拦截方法会报错
* 代理下的目标对象,内部this指向Proxy代理
- - -
# Class
* 定义:对一类具有共同特征的事物的抽象(构造函数语法糖)
* 原理:类本身指向构造函数,所有方法定义在prototype上,可看作构造函数的另一种写法(Class === Class.prototype.constructor)
* 方法和关键字
* `constructor()`:构造函数,new命令生成实例时自动调用
* `extends`:继承父类
* `super`:新建父类的this
* `static`:定义静态属性方法
* `get`:取值函数,拦截属性的取值行为
* `set`:存值函数,拦截属性的存值行为
* 属性
* `__proto__`:构造函数的继承(总是指向父类)
* `__proto__.__proto__`:子类的原型的原型,即父类的原型(总是指向父类的__proto__)
* `prototype.__proto__`:属性方法的继承(总是指向父类的prototype)
* 静态属性:定义类完成后赋值属性,该属性不会被实例继承,只能通过类来调用
* 静态方法:使用static定义方法,该方法不会被实例继承,只能通过类来调用(方法中的this指向类,而不是实例)
* 继承
* 实质
* ES5实质:先创造子类实例的this,再将父类的属性方法添加到this上(Parent.apply(this))
* ES6实质:先将父类实例的属性方法加到this上(调用super()),再用子类构造函数修改this
* super
* 作为函数调用:只能在构造函数中调用super(),内部this指向继承的当前子类(super()调用后才可在构造函数中使用this)
* 作为对象调用:在普通方法中指向父类的原型对象,在静态方法中指向父类
* 显示定义:使用constructor() { super(); }定义继承父类,没有书写则显示定义
* 子类继承父类:子类使用父类的属性方法时,必须在构造函数中调用super(),否则得不到父类的this
* 父类静态属性方法可被子类继承
* 子类继承父类后,可从super上调用父类静态属性方法
* 实例:类相当于实例的原型,所有在类中定义的属性方法都会被实例继承
* 显式指定属性方法:使用this指定到自身上(使用Class.hasOwnProperty()可检测到)
* 隐式指定属性方法:直接声明定义在对象原型上(使用Class.__proto__.hasOwnProperty()可检测到)
* 表达式
* 类表达式:const Class = class {}
* name属性:返回紧跟class后的类名
* 属性表达式:[prop]
* Generator方法:* mothod() {}
* Async方法:async mothod() {}
* this指向:解构实例属性或方法时会报错
* 绑定this:this.mothod = this.mothod.bind(this)
* 箭头函数:this.mothod = () => this.mothod()
* 属性定义位置
* 定义在构造函数中并使用this指向
* 定义在类最顶层
* new.target:确定构造函数是如何调用
## 原生构造函数
* String()
* Number()
* Boolean()
* Array()
* Object()
* Function()
* Date()
* RegExp()
* Error()
## 重点难点
* 在实例上调用方法,实质是调用原型上的方法
* `Object.assign()`可方便地一次向类添加多个方法`(Object.assign(Class.prototype, { ... }))`
* 类内部所有定义的方法是不可枚举的(non-enumerable)
* 构造函数默认返回实例对象(this),可指定返回另一个对象
* 取值函数和存值函数设置在属性的Descriptor对象上
* 类不存在变量提升
* 利用`new.target === Class`写出不能独立使用必须继承后才能使用的类
* 子类继承父类后,this指向子类实例,通过super对某个属性赋值,赋值的属性会变成子类实例的属性
* 使用super时,必须显式指定是作为函数还是作为对象使用
* extends不仅可继承类还可继承原生的构造函数
## 私有属性方法
```js
const name = Symbol("name");
const print = Symbol("print");
class Person {
constructor(age) {
this[name] = "Bruce";
this.age = age;
}
[print]() {
console.log(`${this[name]} is ${this.age} years old`);
}
}
- 定义对象方法
继承混合类
1 | function CopyProperties(target, source) { |