深入ES6 三 变量的解构赋值
3.1 数组的解构赋值解构赋值指的是按照一定规则,直接从数组和对象中提取值,对变量进行赋值。这章主要包括:
- 数组的解构赋值
- 对象的解构赋值
- 嵌套与匹配模式
- 应用
基本用法
解构赋值的本质是模式匹配,只要左右两边的模式相同,左边的变量就会被赋予对应的值。如果解构不成功,则返回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。解构采取贪婪匹配原则,尽量解构更多的数据,并且即使模式不是完全匹配,也能部分解构成功。上面的rest(...)运算符会收录匹配到的其余数据进一个数组,但其必须出现在末尾,否则会报错。
注意:采用结构赋值的写法,变量的声明和赋值是一体的,对于let和const而言,变量不得重新声明,否则会报错。
let [a, b, c] = [1, 2, 3];
let a = 10; // 报错,因为a已经声明过了
再介绍一个乍一眼看上去容易出错的例子:
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]
右值的要求
在进行对数组的解构赋值时,等号右边必须是可遍历的解构,比如说数组,Set集合,甚至Generator对象(会在面的章节介绍)。否则会报错。
字符串在解构时会被转化成一个类似数组的对象,因此可以用于数组的解构赋值
let [a, b, c] = 'hello' // => a=h; b=e; c=l
字符串拥有length属性,我们也可以使用对象的解构赋值来获得之,此时字符串hello会被包装成一个对象:
let {length} = 'hello'
默认值
可以指定解构赋值时的默认值,当右值数组成员严格等于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 [x, y = x] = [1]
2.2 对象的解构赋值
基本用法
let {c, d} = {c:'c, e:'e', d:'d'};
c //=> 'c'
d // => '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,因为它们没有对应的包装类。
对象嵌套的陷阱
如果在解构模式中左值是对象嵌套对象的形式,且外部对象属性没有正确匹配,则在匹配该对象的内部属性时将会报错:
var {foo:{bar}} = {baz:'baz'}
上面的代码中,等号左边对象的foo属性无法正确匹配,所以值为undefined。当继续匹配其中的bar属性时,就会报错,因为我们不能从undefined中取子属性。
2.3 嵌套解构与匹配模式解构赋值本身非常灵活,所以可以进行匹配模式的嵌套:
let {arr:[x,{prop}]} = {arr:[1,{prop:2}]};
console.log(x); // =>1
console.log(prop); // =>2
console.log(arr); // 报错,未定义
上面代码中的arr是匹配模式的一部分,所以不会被赋值。
2.4 应用这里举几个解构赋值非常常见的应用:
交换两个变量的值
[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函数本身默认参数的特性相混淆。
共同学习,写下你的评论
评论加载中...
作者其他优质文章