从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
1 数组的解构赋值
1.1 基本使用
1.1.1 完全解构
条件:等号两边的模式相同
描述:左边所有变量都被赋值,所有值都被提取出来
1 | let [foo, [[bar], baz]] = [1, [[2], 3]]; |
1.1.2 不完全解构
条件:即等号左边的模式,只匹配一部分的等号右边的数组
描述:左边所有变量都被赋值,但没有提取出右边所有的值1
2
3
4
5
6
7
8let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
1.1.3 解构失败
描述:解构没有报错,但存在没被赋值的变量
1 | let [x, y, ...z] = ['a']; |
1.1.4 报错
条件:等号的右边不是数组
扩展:事实上,只要某种数据结构具有Iterator
接口,都可以采用数组形式的解构赋值。
1 | /* 转为对象以后不具备Iterator接口 */ |
1.2 默认值
解构赋值允许指定默认值。
1.2.1 默认值生效条件
通过模式匹配对应的数组成员===
undefined
,默认值才会生效
1 | /* 生效 */ |
1.2.2 默认值使用表达式
会出现惰性求值:即只有在用到的时候(默认值生效),才会求值。
1 | // 不会执行,因为 x 的默认值没生效 |
1.2.3 默认值引用其它变量
注意:该变量必须已经声明,否则报错
1 | /* 正确使用 */ |
2 对象的解构赋值
对象的解构与数组有一个重要的不同
- 数组:元素是按次序排列的,变量的取值由它的
位置
决定 - 对象:属性没有次序,变量必须与属性
同名
,才能取到正确的值
[
var|
let|
const]
{模式:[
赋值变量]
} = {属性1: 属性1值};
2.1 变量必须与属性同名
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者
如果打算让被赋值变量和模式同名,则可以省略模式1
2
3
4
5
6var { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
var { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined
否则1
2
3
4
5
6
7var { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
2.2 变量的声明和赋值是一体的
对于let
和 const
来说,变量不能重新声明,所以一旦赋值的变量以前声明过
1 | let foo; |
2.3 解构嵌套结构的对象
1 | var obj = { |
2.4 指定默认值
默认值生效的条件是,对象的属性值严格等于undefined
1
2
3
4
5
6
7
8
9var {x = 3} = {};
x // 3
var {x, y = 5} = {x: 1};
x // 1
y // 5
var { message: msg = "Something went wrong" } = {};
msg // "Something went wrong"
2.5 解构失败
如果解构失败,变量的值等于undefined
1 | /* 1. 无法匹配到模式 */ |
2.6 已经声明的变量
如果要将一个已经声明的变量用于解构赋值,必须非常小心1
2
3
4var x;
// JavaScript引擎会将{x}理解成一个代码块,从而发生语法错误
{x} = {x: 1};// SyntaxError: syntax error
将整个解构赋值语句,放在一个圆括号里面,就可以正确执行1
2
3var x;
(){x} = {x: 1});
x // 1
3 字符串的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。1
2
3
4
5
6const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。1
2let {length : len} = 'hello';
len // 5
4 数值和布尔值的解构赋值
说明:解构赋值时,如果等号右边是数值和布尔值,则会先转为对象
注意:undefined
和null
无法转为对象,所以对它们进行解构赋值,都会报错
1 | let {toString: s} = 123; |
5 函数参数的解构赋值
函数的参数也可以使用解构赋值
1 | function add([x, y]){ |
5.1 默认值
注意:分两种情况
给解构后被赋值的变量提供默认值
1
2
3
4
5
6
7
8function 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
8function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
6 圆括号
6.1 不能使用圆括号的情形
说明:ES6的规则是,只要有可能导致解构的歧义,就不得使用圆括号。
建议:只要有可能,就不要在模式中放置圆括号。
变量声明语句中,不能带有圆括号
1
2
3
4
5
6
7
8
9// 全部报错
var [(a)] = [1];
var {x: (c)} = {};
var ({x: c}) = {};
var {(x: c)} = {};
var {(x): c} = {};}
var { o: ({ p: p }) } = { o: { p: 2 } };函数参数中,模式不能带有圆括号
函数参数也属于变量声明,因此不能带有圆括号1
2// 报错
function f([(z)]) { return z; }赋值语句中,不能将整个模式,或嵌套模式中的一层,放在圆括号之中
1
2
3
4// 全部报错
({ p: a }) = { p: 42 };
([a]) = [5];
[({ p: a }), { x: c }] = [{}, {}];
6.2 可以使用圆括号的情形
赋值语句的非模式部分
1 | [(b)] = [3]; // 正确 |
7 用途
- 交换变量的值
1 | [x, y] = [y, x]; |
- 从函数返回多个值
1 | // 返回一个数组 |
函数参数的定义
1
2
3
4
5
6
7// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});提取JSON数据
1
2
3
4
5
6
7
8
9
10var jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]函数参数的默认值
1
2
3
4
5
6
7
8
9
10
11jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
}) {
// ... do stuff
};遍历Map结构
(情形一)Iterator配合解构赋值1
2
3
4
5
6
7
8
9var 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
(情形二)如果只想获取键名,或者只想获取键值1
2
3
4
5
6
7
8
9// 获取键名
for (let [key] of map) {
// ...
}
// 获取键值
for (let [,value] of map) {
// ...
}
- 输入模块的指定方法
1
const { SourceMapConsumer, SourceNode } = require("source-map");