ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
数组的结构赋值
- 右边不足
- 左边不足
- 右边为非数组
- 可以有默认值
对象的解构赋值
字符串解构赋值
数值和布尔值的解构赋值
es5:
let a=1,b=2,c=3;
console.log("a:",a); // 1
console.log("b:",b); // 2
console.log("c:",c); // 3
let解构赋值:
let [a,b,c]=[1,2,3];
console.log("a:",a); // 1
console.log("b:",b); // 2
console.log("c:",c); // 3
let [ , , third] = ["foo", "bar", "baz"];
console.log(third); // "baz"
// ...x 相当于x一个数组,把后面没人要的都给它
let [head, ...tail] = [1, 2, 3, 4];
console.log(head); // 1
console.log(tail); // [2, 3, 4]
- 后面赋值项不足的情况
let [foo] = [];
console.log(foo); // undefined
let [bar, foo] = [1];
console.log("bar:",bar); // 1
console.log("foo:",foo); //undefined
let [x, y, ...z] = ['a'];
console.log(x); // "a"
console.log(y); // undefined
console.log(z); // []
- 左侧变量声明不足的情况
let [x, y] = [1, 2, 3];
console.log("x:",x); // 1
console.log("y:",y); // 2
// ...x 相当于x一个数组,把后面没人要的都给它
let [head, ...tail] = [1, 2, 3, 4];
console.log(head); // 1
console.log(tail); // [2, 3, 4]
如果等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错。
// 报错
let [foo] = 1; // Uncaught TypeError: 1 is not iterable
// iterable 可迭代的 iteration 迭代 iterator 迭代器
let [foo] = {}; // Uncaught TypeError: {} is not iterable
let [foo] = null; // Uncaught TypeError: null is not iterable
let [foo] ={a:1}; // Uncaught TypeError: {(intermediate value)} is not iterable
- 默认值
解构赋值允许指定默认值。
let [foo = 1] = [];
console.log(foo); // 1
let [x, y = 'b'] = ['a'];
console.log("x:",x); // a
console.log("y:",y); // b
注:ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。
let [x = 1] = [undefined]; // undefined,相当于没有值,所以去默认值
console.log("x",x); // 1
let [y = 1] = [null]; // null,相当于有值,所以赋值为null
console.log("y:",y); // null;
上面代码中,如果一个数组成员是null,默认值就不会生效,因为null不严格等于undefined。
let [x = 1, y = x] = []; // x=1; y=1
let [x = 1, y = x] = [2]; // x=2; y=2
let [x = 1, y = x] = [3, 2]; // x=3; y=2
let [x = y, y = 1] = []; // ReferenceError: y is not defined
上面最后一个表达式之所以会报错,是因为x用y做默认值时,y还没有声明。
- 对象的解构赋值
let { foo, bar } = { bar: "bbb",foo: "aaa", }; // 顺序是反着的
console.log(foo); // "aaa"
console.log(bar); // "bbb"
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { baz } = { foo: "aaa", bar: "bbb" };
console.log(baz); // undefined
对象解构的的内部机制:eg:
let {foo, bar} = {foo: '111', bar: '222'};
console.log("foo:", foo); // 111
console.log("bar:", bar); // 222
// 等价于
let {foo: foo, bar: bar} = {foo: '111', bar: '222'};
也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。 eg:
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
console.log(baz); // "aaa"
- 对象解构赋值也可以指定默认值
var {x, y = 5} = {x: 1};
console.log("x:",x); // 1
console.log("y:",y); // 5
var {x: y = 3} = {x: 5};
console.log(y); // 5
console.log(x); // Uncaught ReferenceError: x is not defined
上面:x只是为了匹配找值,然后赋给y,所以y=5;但实际是没有声明x变量的!
默认值生效的条件是,对象的属性值严格等于undefined。(与数组是相同的)
- 字符串的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。
let {length : len} = 'hello';
console.log(len); // 5
注:只能用length属性字段,修改为其他的,是要报错的!!!!
- 数值和布尔值的解构赋值
let {toString: s} = 123;
console.log(s); // ƒ toString() { [native code] }
console.log(s===Number.prototype.toString); // true
上面代码中,数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
// Uncaught TypeError: Cannot destructure property `prop` of 'undefined' or 'null'.
let { prop: x } = undefined;
let { prop: y } = null;
- 结构赋值的用途
(1)交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
(2)从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
(3)函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
(4)提取 JSON 数据
解构赋值对提取 JSON 对象中的数据,尤其有用。
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
(5)函数参数的默认值
(6)遍历 Map 结构