解构赋值
解构赋值指的是按照一定规则,直接从数组和对象中提取值,对变量进行赋值。
数组的解构赋值
基本用法
解构匹配
只要左右两边的模式相同,左边的变量就会被赋予对应的值。如果解构不成功,则返回undefined。
let [d,b,c] = [1,2,3]; // => d=1;b=2;c=3
let [[f]] = [[1]]; // =>f=1
let [e,...tail] = [1,2,3,4]; // =>e=1;tail = [2,3,4]
这里let [a, b, c]
表示定义了用于数组解构的三个全局变量a, b, c。解构采取贪婪匹配原则,尽量解构更多的数据,并且即使模式不是完全匹配,也能部分解构成功。
但要注意下面的陷阱:
let [a, [b], c] = [1, [2, 3], 4]; // b=>2
// 如果想让b的值解构后为[2,3],需要写成
let [a,[...b],c] = [1, [2, 3], 4];
// 或者最简单的
let [a, b, c] = [1,[2,3],4]
上面rest(…)运算符必须出现在末尾,否则会报错。
右值的要求
在进行对数组的解构赋值时,等号右边必须是可遍历的解构,比如说数组,Set集合甚至Generator对象。否则会报错。
字符串在解构时会被转化成一个类似数组的对象,因此可以用于数组的解构赋值
let [a, b, c] = 'hello' // => a=h; b=e; c=l
默认值
默认值何时生效
可以指定解构赋值时的默认值,当右值数组成员严格等于undefined时,默认值才会生效。
let [a = 'hello'] = []; //a='hello'
let [a = 'hello'] = [null]; //a=null
惰性
如果默认值是表达式,它的求值方式是惰性的,只有需要使用默认值的时候,才进行计算
let foo = () => {
console.log('fooing');
return 'foo'
};
let [b = foo()] = []; // 会执行foo函数
let [b = foo()] = ['bar']; // 不会执行foo函数
对象的解构赋值
基本用法
let {c, d} = {c:'c,e:'e'',d:'d'};
console.log(c);
console.log(d);
与数组解构的一个不同之处在于对象解构把[]换成了{}, let {c, d}
定义了两个全局变量:c,d。
此外,对象解构赋值是和对象属性的定义顺序无关的,只要名称能够匹配,即左边的变量名与右边的属性名相同,就可以取得正确的值。为了告诉你们这点,我特意把e安排到了d前面。
我不希望把属性名当做变量名
很多时候我们不希望我们的局部变量名和属性名完全相同,那么可以采取下面的办法:
let {foo:baz} = {foo:'foo',bar:'bar'}; // => baz=foo
格式是:
{属性名:变量名... ...}={属性名:值...}
也就是说只需要在属性名后面加”:希望的变量名”就可以了。
那么,你肯定能够想到,我们开始时写的这种形式:
let {c, d} = {c:'c,e:'e'',d:'d'};
其实是下面形式的简写,因为它们的属性名和变量名是相同的。真正被赋值的其实是后者:
let {c:c, d:d} = {c:'c,e:'e'',d:'d'};
使用()告诉引擎我在用解构赋值呢
如果一个已经声明的变量进行解构赋值,大概会像下面这样:
let foo;
{foo} = {foo:'foo'} // 报错
这是因为大括号写在了首行,JS引擎会把它当成一个代码块,解决办法很简单,加上()就可以了:
({foo} = {foo:'foo'})
注意,除了这里以及函数表达式调用的时候可能要用到(),我们使用解构赋值的时候尽量不要使用(),因为它几乎没有用处,还容易导致各种错误出现
对象当然也可以设置默认值了
前面提到过的
let {foo:foo,bar:baz} = {foo:'foo',bar:'bar'}
在进行解构赋值时,实际上是先通过左边的foo,bar属性名去到右边匹配属性,然后把匹配到的属性的值赋给左边的:后的变量名。那么你就不难想到,对象解构可以像下面这样设置默认值:
let {foo='bbb',bar:baz='aaa'} = {foo:'foo',bar:'bar'}
上面的代码foo代表属性名和变量名相同的情况,baz代表不同的情况。
右值的要求
使用对象解构赋值时,右值可以是简单数据类型布尔、字串或者数字。解构时,会先将其转化为对象,再进行解构。
右值不得是不能转成对象的null或undefined,否则会报错。
嵌套解构
解构赋值本身非常灵活,所以可以进行匹配模式的嵌套:
let {arr:[x,{prop}]} = {arr:[1,{prop:2}]};
console.log(x); // =>1
console.log(prop); // =>2
console.log(arr); // 报错,未定义
上面代码中的arr是匹配模式的一部分,所以不会被赋值。
应用
这里举两个比较常见但不提又不容易想到的应用
交换两个变量的值
[foo,bar] = [bar,foo];
定义函数的参数
var foo = ([x, y])=>x+y;
foo([1,2]) // =>3
或者以对象的形式,这样可以优化以配置对象形式编写函数参数方式的代码
var bar = ({x, y, z=5}) => x-y+z
bar({x:1, y:2}) // => 4
但需要注意的是,不要把解构赋值默认参数与ES6函数本身默认参数的特性相混淆。
基本上解构赋值需要注意的点都在这里了。