美文网首页JavaScriptWeb前端之路程序员
Destructuring assignment(解构赋值)in

Destructuring assignment(解构赋值)in

作者: 2f1b6dfcc208 | 来源:发表于2017-08-03 15:45 被阅读68次

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

let x=1,y=2;
[x,y]=[y,x];

与传统的引入中间变量进行值交换的方式相比,这种写法不仅简洁,而且易读,语义非常清晰
2. 解析函数多返回值
JS中函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回,通过解构赋值,我们可以很方便的将这些值取出来并赋给其它变量。

//返回一个数组
function arr(){
    return [1,2,3];
}
let [x,y,z]=arr();
//返回一个对象
function obj(){
    return {
        foo:1,
        bar:2
    }
}
let {foo,bar}=obj();

3. 定义函数多参数传入方式
解构赋值可以很方便地将一组实参与形参变量名对应起来。

//参数是一组有序的值
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);//结果同上,这里可以将每项item看做数组,所以可以用自定义变量获取对应位置的值
}

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

//获取键名
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. 圆括号问题

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

注意:

(1). 当解构赋值与扩展运算符一起使用时,若扩展运算符所在的位置不是最后一个参数,程序会报错。如下:

let { ...x, y, z } = obj; // 句法错误
let { x, ...y, ...z } = obj;//句法错误

反之,只使用了扩展运算符时不会出错

let obj = { x,...y,...z};

(2). 解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型,那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。

相关文章

  • Destructuring assignment(解构赋值)in

    ES6允许按照一定模式,从数组和对象(以及字符串、数值、布尔值、函数参数等)中提取值,按照对应位置给变量进行赋值,...

  • ES6基本的语法(四) destructuring ES6 解构

    destructuring 解构赋值 解构也叫解构化赋值:解构过程中,具备赋值和变量声明两个功能。目的在于把等号左...

  • 解构赋值Destructuring(上)

    全局变量## let命令声明的函数在严格模式下不属于全局变量 解构 数组解构## 对应位置解构## 默认值 非遍历...

  • 解构赋值(Object Destructuring)

    参考文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScr...

  • [ES6] 解构赋值

    解构(Destructuring) ES6 允许从数组和对象中提取值,对变量进行赋值,这被称为解构(Destruc...

  • ES6浅记录

    解构赋值从对象或数组中提取数值,赋值于另一个变量。(Destructuring); symbol //标识 新增的...

  • ES6 Destructuring Assignment (解构

    数组的解构赋值 更简单明了地定义变量 设定默认值 如果一个数组成员是null,默认值就不会生效,因为null不严格...

  • ES6 解构

    解构 ES6 新增了解构( destructuring ),它按照一定模式,从数组和对象中提取值,对变量进行赋值,...

  • ES6数组的解构赋值

    解构(Destructuring)。是指在ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值, 在解构出...

  • ES6——解构赋值(Destructuring)

    数组的解构赋值 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructur...

网友评论

    本文标题:Destructuring assignment(解构赋值)in

    本文链接:https://www.haomeiwen.com/subject/knuelxtx.html