ES6允许按照一定的模式,从数组对象和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
三个规则
- 如果等号右边不是对象,会尝试转换为对象
只有当等号右边对应的属性值严格等于undefined的时候,等号左边的默认值才能生效
如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
1
2
3
4
5function f() {
console.log('aaa');
}
let [x = f()] = [1];数组的解构赋值要求等号右边的对象具备Iterator接口,即只要某种数据结构具备Iterator接口,都可以采用数组的形式解构。
以下数据结构原声具备Iterator接口:
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
Generator 函数
1
2
3
4
5
6
7
8
9
10
11function* fibs() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
let [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5
圆括号问题
解构赋值虽然很方便,但是对于编译器来说,一个式子到底是模式,还是表达式,必须解析到(或解析不到)等号才能知道。
由此带来的问题是,如果模式中出现圆括号怎么处理。ES6 的规则是,只要有可能导致解构的歧义,就不得使用圆括号。
但是,这条规则实际上不那么容易辨别,处理起来相当麻烦。因此,建议只要有可能,就不要在模式中放置圆括号。因此,建议只要有可能,就不要在模式中放置圆括号。
使用圆括号
只有一种情况可以使用圆括号:赋值语句的非模式部分,可以使用圆括号。1
2
3
4
5
6// 模式是取数组的第一个成员,跟圆括号无关
[(b)] = [3]; // 正确
// 模式是p
({ p: (d) } = {}); // 正确
// 同上面第一句
[(parseInt.prop)] = [3]; // 正确
上面三行语句都可以正确执行,因为首先它们都是赋值语句,而不是声明语句;其次它们的圆括号都不属于模式的一部分。第一行语句中,模式是取数组的第一个成员,跟圆括号无关;第二行语句中,模式是p,而不是d;第三行语句与第一行语句的性质一致。
在赋值语句中,等号左边数组的非下标部分和对象的非属性部分可以使用圆括号
不适用圆括号
以下三种解构赋值不得使用圆括号。
变量声明语句
1
2
3
4
5
6
7
8
9// 全部报错
let [(a)] = [1];
let {x: (c)} = {};
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};
let { o: ({ p: p }) } = { o: { p: 2 } };函数参数
函数参数也属于变量声明,因此不能带有圆括号。
1
2
3
4// 报错
function f([(z)]) { return z; }
// 报错
function f([z,(x)]) { return x; }赋值语句的模式
1
2
3// 全部报错
({ p: a }) = { p: 42 };
([a]) = [5];
用途
交换变量的值
1
2
3
4let x = 1;
let y = 2;
[x, y] = [y, x];函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
1
2
3
4
5
6
7// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});提取 JSON 数据
解构赋值对提取 JSON 对象中的数据,尤其有用。
1
2
3
4
5
6
7
8
9
10let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]函数参数的默认值
指定参数的默认值,就避免了在函数体内部再写
var foo = config.foo || 'default foo';
这样的语句。遍历map结构
任何部署了Iterator接口的对象,都可以用for…of循环遍历。Map 结构原生支持Iterator接口,配合变量的解构赋值,获取键名和键值就非常方便。
1
2
3
4
5
6
7const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}提取数组的头尾的值
数组也是对象的一种,下面这种方法对于数组和类数组对象尤其有用
1
2
3
4
5let arr = [1, 23, 4, 5, 5, 6];
let { 0: first, [arr.length - 1]: last } = arr;
console.log(first, last);