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]
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" };
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
2
3
4
3、字符串的解构赋值
字符串被转换成了一个类似数组的对象。
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
2
3
4
5
6
类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。
let {length : len} = 'hello';
len // 5
2
4、 数值和布尔值的解构赋值
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
2
3
4
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。
由于undefined
和null
无法转为对象,所以对它们进行解构赋值,都会报错。
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
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]
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]
2
3
4
5
6
7
上面代码是为函数move的参数(形参)指定默认值,而不是为变量x和y指定默认值,所以会得到与前一种写法不同的结果。这种写法直接是将所传参数将默认参数进行覆盖。用实参将{x:0,y:0}
覆盖。
上面两种写法本质上都是用所传参数将默认参数进行覆盖。
6、 解构赋值的用处
1、交换变量的值。
let{x,y}={y,x};
2、从函数返回多个值;
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
2
3
4
5
6
7
3、函数参数的定义;
function f([x, y, z]) { ... }
f([1, 2, 3]);
2
4、提取json数据
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
2
3
4
5
6
5、输入模块的指定方法;
const { SourceMapConsumer, SourceNode } = require("source-map");
6、函数参数的默认值(这样避免了在函数内部再设置默认值)
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
} = {}) {
// ... do stuff
};
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) {
// ...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16