Destructuring assignment(解构赋值)in ES6

弧度里的微笑 · · 2810 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

  ES6允许按照一定模式,从数组和对象(以及字符串、数值、布尔值、函数参数等)中提取值,按照对应位置给变量进行赋值,这被称为解构赋值。首先,假定你已经了解了什么是解构赋值,我们先来快速看一下它的常见用途。

  1. 交换变量的值
let x=1,y=2;
[x,y]=[y,x];

与传统的引入中间变量进行值交换的方式相比,这种写法不仅简洁,而且易读,语义非常清晰

  1. 解析函数多返回值
    JS中函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回,通过解构赋值,我们可以很方便的将这些值取出来并赋给其它变量。
//返回一个数组
function arr(){
    return [1,2,3];
}
let [x,y,z]=arr();
//返回一个对象
function obj(){
    return {
        foo:1,
        bar:2
    }
}
let {foo,bar}=obj();
  1. 定义函数多参数传入方式
    解构赋值可以很方便地将一组实参与形参变量名对应起来。
//参数是一组有序的值
function f([x,y,z]){...}
f([1,2,3]);
//参数是一组无序的值
function f({x,y,z}){...}
f({y:2,x:1,z:3});

4.提取JSON数据
解构赋值对提取JSON对象中数据,尤其有用。

let jsonData={
    id:20,
    status:'OK',
    data:[12,34]
};
let {id,status,data:number}=jsonData;
console.log(id,status,number);//20 'OK' [12,34]

5.遍历部署了Iterator接口的数据结构时,即在for ... of循环遍历自身每一项时,可以直接多参数传值。Map结构原生支持Iterator接口,使用变量的解构赋值,可以很方便的获取键名和键值。

var map=new Map();
map.set('first','hello');
map.set('second','world');
for(let item of map){
    console.log(item);
}
for(let [key,value] of map){
    console.log(key+'is'+value);
}
for(let [i,j] of map){
    console.log(i+' is '+j);//结果同上
}

如果只想获取键名,或者只想获取键值,可以写成下面这样

//获取键名
for (let [key] of map){...}
//获取键值
for (let [,value] of map){...}

6.获取模块的指定方法
加载模块后,往往需要再次指定变量获取模块的某些方法。解构赋值直接精简了这一过程。

const {SourceMap,SourceNode} = require('source-map');

接下来详细介绍Destructuring assignment.

1. 数组的解构赋值

以前,为变量赋值,只能直接指定值

let a=1,b=2,c=3;

现在ES6允许这样

let [a,b,c]=[1,2,3]

看起来和golang的多参数赋值差不多,这里的代码实际上表示从数组中提取值,按照对应位置赋给变量。本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

let [foo,[[bar],bar]]=[1,[[2],3]];
let [,,third]=['foo','bar','baz']
let [x,,y]=[1,2,3]
let [head,...tail]=[1,2,3,4];
let [x,y,...z]=['a'];

如果解构不成功,变量的值将等于undefined。若等号右边的值不是可遍历的数据结构(没有Iterator接口),那么将会报错

//报错
let [foo]=1;
let [foo]=undefined;
let [foo]=null;
let [foo]=false;
let [foo]=NaN;
let [foo]={};//注意,普通对象本身不具备Iterator接口,不可遍历
//for循环遍历,数组和对象可使用for...in遍历,其中数组遍历时传入的参数值是索引,对象传入的是属性,数组还可以使用forEach方法进行遍历

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

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

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

function* fibs(){
    let a=0,b=1;
    while(true){
        yield a;
        [a,b]=[b,a+b];
    }
}
let [first,second,third,fourth,fifth,sixth]=fibs();
console.log(sixth);//5

上面代码中,fibs是一个Generator函数,原生具有Iterator接口,解构赋值会依次从这个接口获取值。
解构赋值允许指定默认值,只有当数组成员严格等于undefined时,默认值才会生效。如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。

function f(){...}
let [x=f()]=[1];

上面的代码中,因为x能取到值,所以函数f根本不会执行。

2. 对象的解构赋值

解构赋值不仅可以用于数组,还可以用于对象

let {foo,bar}={foo:'aaa',bar:'bbb'}

比较常见的是在Node导出模块对象时:

let a=1,b=2;
//let obj={a,b}
module.exports={a,b}

对象的解构与数组有一个重要的不同,数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名才能取到正确的值。

let {foo,bar}={bar:'bbb',foo:'aaa'}
let {baz}={foo:'aaa',bar:'bbb'}

上面的第一行语句,等号左边的两个变量的次序与等号右边两个同名属性的次序不一致,但是对取值完全没有影响,第二行语句中左边的变量在等号右边没有对应的同名属性名,所以取不到值,为undefined
若所需要的变量名明确与属性名不一致,则必须写成下面这样:

let {foo:baz}={foo:'aaa'};
console.log(baz);//aaa

let {first:f,last:l}={first:'hello',last:'world'}
console.log(f,l);//hello world

这实际上说明,对象的解构赋值是下面形式的简写:

let {foo:foo,bar:bar}={foo:'aaa',bar:'bbb'}

也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再将该属性的值赋给模式匹配的变量,真正被赋值的是后者,而不是前者。对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。
由于数组本质是一种特殊的对象,因此可以对数组进行对象属性的解构

let arr=[1,2,3];
let {0:first,[arr.length-1]:last}=arr;//方括号这种写法,属于“属性名表达式”
console.log(first,last);//1 3

3. 字符串的解构赋值

字符串也可以解构赋值,这是因为字符串此时会先被默认转化为一个类似数组的对象

const [a,b,c,d,e]='hello';
console.log(a,b,c,d,e);
let {length:len}='hello';
console.log(len);// 5 类似数组的对象都有一个length属性

4. 数值和布尔值的解构赋值

解构赋值时,如果等号右边是数值和布尔值,则会先转化为对象。

let {toString:s}=123;
console.log(s);//Number.prototype.toString

let {toString:s}=true;
console.log(s);//Boolean.prototype.toString

解构赋值的规则是,如果等号右边的规则不是对象或者数组时,就先将其转化为对象。若无法转化为对象,如nullundefined,则会报错

5. 函数参数的解构赋值

函数的参数也可以使用解构赋值

function add([x,y]){...}
add([1,2]);

上面代码中,函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量x和y,对于函数内部的代码来说,它们能感受到的参数就是x和y.
下面是另一个例子:

[[1,2],[3,4]].map(([x,y])=>x+y);//[3,7]

函数参数的解构赋值也可以使用默认值

function move({x=0,y=0}={}){
    return [x,y]
}
move();//[0,0]
move({});//[0,0]
move({x:2});//[2,0]
move({x:2,y:3});//[2,3]
//只有当传入参数值为`undefined`时,才会考虑默认值,在阅读的时候可以先抛开默认值不看便于阅读

上面代码中,函数move的参数是一个对象,同时设置默认值为{},通过对传入的对象进行解构,得到变量xy的值,如果解构失败,xy等于默认值。
注意,下面的写法略微有点不同

function move({x,y}={x:0,y:0}){
     return [x,y]
}
move();//[0,0]
move({});//[undefined,undefined]
move({x:2});//[2,undefined]
move({x:2,y:3});//[2,3]

这里函数参数的默认值为{x:0,y:0},与上面不同。

6. 圆括号问题

在变量声明语句,函数参数以及赋值语句的模式之中,都不能使用等号,只有一种情况下可以使用圆括号:赋值语句的非模式部分。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:弧度里的微笑

查看原文:Destructuring assignment(解构赋值)in ES6

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2810 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传