ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。本质上,这种写法属于模式匹配,只要等号两边的模式相同,左边的变量就会被赋予对应的值。 如果解构不成功,变量的值就等于undefined。

1、数组的结构赋值

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

let [foo = true] = [];foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
let [x = 1] = [null]; x //null null不严格等于undefined,但是null==undefined
let [x = 1, y = x] = [];     // x=1; y=1
let [x = 1, y = x] = [2];    // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];     //  y is not undefined
//从左到右的读取。
let [x , y] = [];//[undefined,undefined]
1
2
3
4
5
6
7
8
9
10

   注意:ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。如果一个数组成员是null,默认值就不会生效,因为null不严格等于undefined。

2、 对象的解构赋值

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

let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: "aaa", bar: "bbb" };baz // undefined
let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz//"aaa",foo是匹配的模式,baz才是变量名

let { foo, bar } = { foo: "aaa", bar: "bbb" };
//是下面表示的简写。
let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
1
2
3
4
5
6
7
8
9
10

   由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。数组arr的0键对应的值是1,[arr.length - 1]就是2键,对应的值是3。方括号这种写法,属于属性名表达式

let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
console.log(first) // 1
console.log(last) // 3
1
2
3
4

3、字符串的解构赋值

   字符串被转换成了一个类似数组的对象。

const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
1
2
3
4
5
6

   类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

let {length : len} = 'hello';
len // 5
1
2

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

let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
1
2
3
4

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。 由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。

let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
1
2

5、 函数参数的解构赋值

function move({x = 0, y = 0} = {}) {
        return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
1
2
3
4
5
6
7

   上面代码中,函数move为变量x和y指定默认值,函数move的参数是一个对象,通过对这个对象进行解构,得到变量x和y的值。如果解构失败,x和y等于默认值。用实参将{}覆盖。

function move({x, y} = { x: 0, y: 0 }) {
        return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined],相当于{x,y}={x,3}
move({}); // [undefined, undefined],相当于{x,y}={};
move(); // [0, 0]
1
2
3
4
5
6
7

   上面代码是为函数move的参数(形参)指定默认值,而不是为变量x和y指定默认值,所以会得到与前一种写法不同的结果。这种写法直接是将所传参数将默认参数进行覆盖。用实参将{x:0,y:0}覆盖。
上面两种写法本质上都是用所传参数将默认参数进行覆盖。

6、 解构赋值的用处

1、交换变量的值。

let{x,y}={y,x};    
1

2、从函数返回多个值;

function example() {
    return {
        foo: 1,
        bar: 2
    };
}
let { foo, bar } = example();
1
2
3
4
5
6
7

3、函数参数的定义;

function f([x, y, z]) { ... }
f([1, 2, 3]);
1
2

4、提取json数据

let jsonData = {
    id: 42,
    status: "OK",
    data: [867, 5309]
};
let { id, status, data: number } = jsonData;
1
2
3
4
5
6

5、输入模块的指定方法;

const { SourceMapConsumer, SourceNode } = require("source-map");
1

6、函数参数的默认值(这样避免了在函数内部再设置默认值)

jQuery.ajax = function (url, {
        async = true,
        beforeSend = function () {},
        cache = true,
        complete = function () {},
        crossDomain = false,
        global = true,
        // ... more config
} = {}) {
        // ... do stuff
};
1
2
3
4
5
6
7
8
9
10
11

7、遍历map结构(map原生支持iterator接口)

const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
        console.log(key + " is " + value);
}
// first is hello
// second is world
// 获取键名
for (let [key] of map) {
        // ...
}
// 获取键值
for (let [,value] of map) {
        // ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Last Updated: 3/16/2020, 6:57:04 PM