参考文档:https://es6.ruanyifeng.com/
解构:ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构
- 本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
- 解构不成功,左边变量没有在等号右侧找到对应的数据,变量的值就等于
undefined
。 - 不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功
- 如果等号的右边不是数组(或者严格地说,不是可遍历的结构)将会报错。Set 结构,也可以使用数组的解构赋值。只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值
例子如下:
尤其注意:数组解构时...的写法相当于取子集
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
let [a] = 1;
//TypeError: 1 is not iterable
let [x, y, z] = new Set([1,2,3,4]);
console.log(x,y,z);
//输出
1 2 3
4、解构赋值允许指定默认值。
let [a=true] = [];
console.log(a);
//输出
true
let [x, y = 'b'] = ['a', undefined];
console.log(x,y);
//输出
a b
let [z=2] = [1];
console.log(z);
//输出
1
注意:当匹配到的值是undefined,默认值会生效;只有当一个数组成员严格等于undefined
,默认值才会生效。
5、默认值是表达式惰性求值,只有用到才会求值
function f() {
return 2;
}
let [x = f()] = [3];
console.log(x);
//输出 3
x
能取到值3,所以函数f
根本不会执行
6、默认值可以引用解构赋值的其他变量,但该变量必须已经声明
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] = []; // ReferenceError: y is not defined
二、对象的解构赋值
对象的解构与数组有一个重要的不同。
数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
没有对应的同名属性导致取不到值,最后等于undefined
。
let {a,b} = { b: 'b',a: 'a' };
a //'a'
b //'b'
let {a} = {b:1}
a // undefined
1、对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量
const { log } = console;
log(1)
//输出 1
注意,这里看上去和常见的解构没有一一对应,但是console也是对象
2、如果变量名与属性名不一致,必须写成下面这样
let { a: x } = { a:1 };
// x=1
3、内部机制
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者
let { a: x } = { a:1 };
console.log(a);
//ReferenceError: a is not defined
4、解构也可以用于嵌套结构的对象,类似数组
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p, p: [x, { y }] } = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]
注意:第一个p匹配obj内部的所有值,第2个p是模式
5、如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错
let {a:{b}} = {b: 2};
//TypeError: Cannot destructure property `b` of 'undefined' or 'null'
6、对象的解构赋值可以取到继承的属性
const obj1 = {};
const obj2 = { foo: 'bar' };
Object.setPrototypeOf(obj1, obj2);
const { foo } = obj1;
console.log(foo)
// "bar"
7、对象的解构也可以指定默认值并且默认值生效的条件是,对象的属性值严格等于undefined
。
8、注意:
(1)如果要将一个已经声明的变量用于解构赋值
// 错误的写法
let x;
{x} = {x: 1};
// SyntaxError: Unexpected token =
报错原因:JavaScript 引擎会将{x}
理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题
// 正确的写法
let x;
({x} = {x: 1});
(2)数组本质是特殊的对象,因此可以对数组进行对象属性的解构。注意模式是下标
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
console.log(first, last)
//输出:1 3
字符串的解构
字符串被转换成了一个类似数组的对象进行解构
const [a, b, c, d, e] = 'hello';
console.log(a,b,c,d,e)
// h e l l o
数值和布尔值的解构赋值
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。
let {toString: s} = 123;
s === Number.prototype.toString // true
函数参数的解构赋值
圆括号问题
解构赋值虽然很方便,但是解析起来并不容易。对于编译器来说,一个式子到底是模式,还是表达式,没有办法从一开始就知道,必须解析到(或解析不到)等号才能知道。
由此带来的问题是,如果模式中出现圆括号怎么处理。ES6 的规则是,只要有可能导致解构的歧义,就不得使用圆括号。
但是,这条规则实际上不那么容易辨别,处理起来相当麻烦。因此,建议只要有可能,就不要在模式中放置圆括号。
变量解构赋值用途
1、交换变量值
let x = 1;
let y = 2;
[x, y] = [y, x];
console.log(x,y)
//2 1
2、从函数返回多个值
js函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便
3、函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
4、提取 JSON 数据
5、函数参数的默认值
6、遍历 Map 结构
任何部署了 Iterator 接口的对象,都可以用for...of
循环遍历。
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) {
// ...
}
7、输入模块的指定方法
加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰
const { SourceMapConsumer, SourceNode } = require("source-map");