为了账号安全,请及时绑定邮箱和手机立即绑定

深入ES6 三 变量的解构赋值

标签:
JavaScript
解构赋值

解构赋值指的是按照一定规则,直接从数组和对象中提取值,对变量进行赋值。这章主要包括:

  1. 数组的解构赋值
  2. 对象的解构赋值
  3. 嵌套与匹配模式
  4. 应用
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函数本身默认参数的特性相混淆。

点击查看更多内容
7人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
Web前端工程师
手记
粉丝
316
获赞与收藏
3231

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消