变量的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
从这句话中可以知道,解构的对象主要有两种:数组和对象。 但其实字符串,布尔值和数值都可以进行解构赋值,甚至包括函数的参数。
1. 数组
基本用法:
let [a, b, c] = [1, 2, 3];
等同于这么写:
let a = 1;
let b = 2;
let c = 3;
只要等号左右两边的模式相同,左边的变量就会被赋予对应的值,如果解构不成功,变量的值就等于undefined。如下图:
let [foo] = [];
let [bar, foo] = [1];
foo的结果为undefined。
但如果等号右边不是数组,或者严格来说,不是可遍历的结构,就会报错。
默认值
所谓默认值,我的理解是当等号右边的数组中存在没有赋予新值的位置,并且等号左边的数组里存在等号,那么左边数组中对应的位置上的值就被保留。这种说法比较复杂,看代码就会很清楚,如:
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
只有当一个数组成员严格等于undefined
,默认值才会生效。如果一个数组成员是null
,默认值就不会生效,因为null
不严格等于undefined
。
另外,如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
也就是说,如果等号右边有值可赋,那左边数组里的函数根本不会执行。
function f() {
return 2;
}
let [x = f()] = [1]; // [x]=[1]
2. 对象
基本用法
和数组的解构赋值类似。
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
但有一点不同,即数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
上述代码其实是下面的形式简写:
let { foo: fooV, bar: barV} = { foo: 'aaa', bar: 'bbb' };
fooV // "aaa"
foo // error: foo is not defined
barV // "bbb"
也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。因此,foo是匹配的模式,fooV才是变量,真正赋值的是变量fooV。
默认值
对象的解构也可以指定默认值。默认值生效的条件是,对象的属性值严格等于undefined
。
注意
- 如果要将一个已经声明的变量用于解构赋值,需要将整个解构赋值语句,放在一个圆括号里面。
// 错误的写法
let x;
{x} = {x: 1};
// SyntaxError: syntax error
// 正确的写法
let x;
({x} = {x: 1});
因为 JavaScript 引擎会将{x}
理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。
- 由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
3. 字符串、数值、布尔值和函数参数
字符串可以解构赋值是因为字符串被转换成了一个类似数组的对象。
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
let {length : len} = 'hello';
len // 5
数值和布尔值的解构规则是:只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
函数的参数也可以使用解构赋值,函数参数的解构赋值如下代码所示:
function move({x = 0, y = 0} = {}) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
参考文章: ES6入门——变量的解构赋值,作者:阮一峰