解构赋值:

  • 通过解构赋值, 可以将属性或值从对象或数组中取出,赋值给其他变量。
  • 简单理解就是等号的左边和右边相等。主要分为对象的解构和数组的解构。

在没有解构赋值的时候,我们赋值是这样的

let arr = [0, 1, 2]
let a = arr[0]
let b = arr[1]
let c = arr[2]

===

// 解构写法
let [a, b, c] = [0, 1, 2]

数组的解构赋值

数组解构的基本用法

let [a, b, c] = [1, 2, 3] // a=1, b=2, c=3
let [d, [e], f] = [1, [2], 3] // 嵌套数组解构 d=1, e=2, f=3
let [g, ...h] = [1, 2, 3] // 数组拆分 g=1, h=[2, 3]
let [i, , j] = [1, 2, 3] // 不连续解构 i=1, j=3
let [k, l] = [1, 2, 3] // 不完全解构 k=1, l=2

变量声明并赋值时的解构

let arr = [0, 1, 2]
let [a, b, c] = arr
console.log(a) // 0
console.log(b) // 1
console.log(c) // 2

变量先声明后赋值时的解

通过解构分离变量的声明,可以为一个变量赋值。

var a, b;

[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2

使用默认值

为了防止从数组中取出一个值为 undefined 的对象,可以在表达式左边的数组中为任意对象预设默认值。

let arr = [, 1, 2] // 等价于 let arr1 = [undefined, 1, 2]
let [a = '我是默认值', b, c] = arr
console.log(a) // '我是默认值'
console.log(b) // 1
console.log(c) // 2

在解构赋值的过程中,a=undefined 时,会使用默认值

ES6 内部使用严格相等运算符(​​===​​​),判断一个位置是否有值。所以,只有当一个数组成员严格等于​​undefined​​​,默认值才会生效。如果一个数组成员是​​null​​​,默认值就不会生效,因为​​null​​​不严格等于​​undefined​​。

let arr1 = [null, 1, 2]
let [a = '我是默认值', b, c] = arr1
console.log(a) // null
console.log(b) // 1
console.log(c) // 2

默认值可以引用解构赋值的其他变量,但该变量必须已经声明。

let [x = 1, y = x] = [];     // x=1; y=1
let [x = 1, y = x] = [2]; // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = []; // ReferenceError: y is not defined

上面最后一个表达式之所以会报错,是因为​​x​​​用​​y​​​做默认值时,​​y​​还没有声明。

如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。

function f() {
console.log('aaa');
}

let [x = f()] = [1];

上面代码中,因为​​x​​​能取到值,所以函数​​f​​根本不会执行。上面的代码其实等价于下面的代码。

let x;
if ([1][0] === undefined) {
x = f();
} else {
x = [1][0];
}

解析一个从函数返回的数组

从一个函数返回一个数组是十分常见的情况。解构使得处理返回值为数组时更加方便。在下面例子中,要让 [1, 2] 成为函数的 f() 的输出值,可以使用解构在一行内完成解析。

function f() {
return [1, 2];
}

var a, b;
[a, b] = f();
console.log(a); // 1
console.log(b); // 2

===============

// 忽略某些返回值
function f() {
return [1, 2, 3];
}

var [a, , b] = f();
console.log(a); // 1
console.log(b); // 3

===============

// 忽略全部返回值:
[,,] = f();

将剩余数组赋值给一个变量

如果剩余元素右侧有逗号,会抛出 SyntaxError,因为剩余元素必须是数组的最后一个元素。

var [a, ...b] = [1, 2, 3];
console.log(a); // 1
console.log(b); // [2, 3]

============

var [a, ...b,] = [1, 2, 3];
// SyntaxError: rest element may not have a trailing comma

数组的拼接

let a = [0, 1, 2]
let b = [3, 4, 5]
let c = a.concat(b)
console.log(c) // [0, 1, 2, 3, 4, 5]

let d = [...a, ...b]
console.log(d) // [0, 1, 2, 3, 4, 5]

数组的克隆

// 数组的克隆
// 假如我们简单地把一个数组赋值给另外一个变量
let a = [0,1,2,3]
let b = a
b.push(4)
console.log(a) // [0,1,2,3,4]
console.log(b) // [0,1,2,3,4]
// 这只是简单的把引用地址赋值给b,而不是重新开辟一个内存地址
// 所以a和b共享了同一个内存地址,
// 该内存地址的更改,会影响到所有引用该地址的变量

用下面的方法,把数组进行克隆一份,互不影响

let a = [0,1,2,3]
let b = [...a]
b.push(4)
console.log(a) // [0,1,2,3]
console.log(b) // [0,1,2,3,4]

等号的右边不是数组

如果等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错。 

// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

上面的语句都会报错,因为等号右边的值,要么转为对象以后不具备 Iterator 接口(前五个表达式),要么本身就不具备 Iterator 接口(最后一个表达式)。

其他结构的数据

对于 Set 结构,也可以使用数组的解构赋值。

let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"

事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。

function* 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

上面代码中,​​fibs​​是一个 Generator 函数,原生具有 Iterator 接口。解构赋值会依次从这个接口获取值。