变量的解构赋值
定义:ES6允许按照一定的模式,从数组和对象中提取值,并赋值给变量
数组的解构赋值
公式:[variable = defaultVal] = [value]
- 🥝解构赋值属于(部分)模式匹配,只要等号两边的模式相同,右边的变量就会赋予对应的值
- 解构失败,变量的值为undefined
- 🥞若右边不是数组(非可遍历结构,无iterator接口),将会报错
- 🥙解构允许指定默认值,仅当右侧数组元素严格等于
===
undefined时,默认值才有效- 🧀默认值可引用解构赋值的其他变量,但该变量必须已经声明;在默认值不会生效时,即使是未声明变量,也不会报错
- 默认值可以是函数、其他变量等等
javascript
// 🥝:模式匹配
let [a, b, c] = [1, 2, 3]
let [a, [b], c] = [1, [2], 3]
let [ , , c] = [1, 2, 3]
let [a, , c] = [1, 2, 3]
// a: 1, c: [2, 3]
let [a, ...c] = [1, 2, 3]
// a: 1, b: undefined, c: []
let [a, b, ...c] = [1]
// 🥝:部分模式匹配
let [a, b] = [1, 2, 3]
let [a, [b], c] = [1, [2], 3]
// 🥞:TypeError: 1 is not iterable
let [a] = 1
// 🥙:指定默认值
let [a = true] = []
let [a, b = true] = [1, undefined]
let [a = 1] = [null] // a: null
// 🧀:引用其他默认值
let [a = 1, b = a] = [1]
let [a = b, b = 1] = [1, 2]
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
对象的解构赋值
公式:{ mode: variable = defaultVal} = { mode: value} || { mode = defaultVal } =
- 🍵变量需要与对象属性同名,才能取到正确的值
- 本质上左右两边都是键值对的形式,查询相同的键,并赋值
- 无对应同名属性,值为undefined
- 🍂可将对象(比如Math、string)的方法赋给变量
- 🧊等式左边的键属于模式
- 对象解构赋值可以取到继承的属性
- 🍜由于数组是特殊的对象,可对数组进行对象的属性解构
- 🧃对已经声明的变量进行赋值解构需使用圆括号将整个表达式括起来,因为js引擎会将大括号理解成一个代码块,从而发生语法错误(SyntaxError: Unexpected token '=')
- 解构赋值允许等号左边的模式中不放任何变量,故而会有下面的都正确的奇怪表达式:
- ({} = [true, false]);
- ({} = 1);
- ({} = []);
- 对象的普通变量与扩展运算符
...
结合使用时的解构赋值情形:{ ...obj } = {a: 1, b: 2, c: 3} => obj: {a: 1, b: 2, c: 3}
- 对象的解构赋值,将目标对象自身的可遍历属性、尚未被读取的属性分配到指定的对象中
- 解构赋值等式右侧必须是一个对象,当为undefined、null等常量时,会报错,其无法转为对象
- 解构赋值必须是最后一个参数
- 解构赋值的拷贝是浅拷贝,当对象属性值为一个符合类型时,拷贝的是该属性的引用
- 解构赋值,不能复制继承自原型的属性
- 解构赋值可用于扩展函数的参数,对函数进行增强操作🍧
案例:
javascript
// 🍵
let {a, b} = {a: 1, b: 2}
// 等同于上面
let {a: a, b: b} = {a: 1, b: 2}
let {c} = {a: 1} // c: undefined
// 🍂
let {random, floor, ceil} = Math
// 🧊
// a: ReferenceError: a is not defined b: 1
let {a: b} = {a: 1}
// 🧊:嵌套解构
let a = {
b: [1, {
c: 2
}]
}
// b: ReferenceError: a is not defined d: 1, c: 2
let {b: [d, {c}]} = a
// b: [1, {c: 2}] d: 1, c: 2
let {b, b: [d, {c}]} = a
// 🧊
const node = {
loc: {
start: {
line: 1,
column: 5
}
}
}
// 分三次解构
// 第一次:loc是模式,loc是变量
// 第二次:loc是模式,start是变量
// 第三次:loc、start是模式,line是变量
let { loc, loc: { start }, loc: { start: { line }} } = node
// 🧊TypeError: Cannot destructure property 'gdt' of 'undefined' as it is undefined.
// 解构步骤:
// 1,foo: undefined
// 2,{bar} = undefined
let {foo: {bar}} = {baz: 'baz'}
// 🍜
let a = [1, 2, 3]
let { 0: first, [arr.length - 1]: last} = a
// first: 1, last: 3
// 🧃
// SyntaxError: Unexpected token '='
let a
{a} = {a: 1}
// success
let a
({a} = {a: 1})
// 🍧解构赋值结合扩展运算符使用,对函数进行扩展
function baseFunction ({a, b}) {
// 函数操作
}
function wrapperFunction ({x, y, ...other}) {
// 引入的其他增强操作
// .......
return baseFunction(other)
}
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
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
字符串的解构赋值
- 🍖字符串解构时会转成类似数组的对象,含有length属性
- 数组解构:
- 对象解构:
案例:
javascript
// 数组解构
// a: h, b: e, c: l
let [a, b, c] = 'hello'
// 对象解构:
// a: h, length: 5
let {0: a, length} = 'hello'
1
2
3
4
5
6
7
2
3
4
5
6
7
数值和布尔值的解构赋值
- 数值和布尔值解构时,均会先转为包装对象,包装对象具有toString属性
- 由于undefined和null无法转为对象,解构赋值会报错
案例:
javascript
// toString: Number.prototype.toString
let {toString} = 123
//
let {}
1
2
3
4
2
3
4
函数参数的解构赋值
- 函数参数解构:
- ([a, b]) <= ([1, 2])
- 为变量指定默认值:({a = 1, b = 2} = {}) <= ({a: 1, b: 3})
- 为函数参数指定默认值:({a, b} = {a: 1, b: 3}) < ({a: 1, b: 2})
- undefined会触发函数参数的默认值
圆括号问题
- 编译器不会事先知道一个式子是表达式还是模式,必须解析(不)到等号才知道
- ES6中,只要可能导致解构歧义,就不能使用圆括号,建议不要在解构模式内部放置圆括号,不能待圆括号的有:
- 函数参数解构
- 赋值语句中的模式(部分或整体),无变量标志的
- 变量声明语句(带有变量标志的let、var等):左侧内部
[(a)]
,对象的左侧内部{(x: c)}
、左侧外部({x: c})
、左侧值{x: (c)}
、左侧键{(x): c}
- 可以使用圆括号的:
- 赋值语句的非模式部分(模式是指(数组的index、对象的key)
解构赋值的用途
- 交换变量:[a, b] = [b, a]
- 从函数返回多个值,节省代码长度:[a,b] = (function() {return [1, 2]})()