数组的解构赋值
ES6规定以一定模式从数组、对象中提取值,然后给变量赋值叫做解构。
本质上就是一种匹配模式,等号两边模式相同,左边的变量就能对应的值。
假如解构不成功会赋值为undefined。
不需要匹配的位置可以置空
js
let [ a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3
let [, , d] = [1, 2, 3];
// d = 3
1. 不完全解构
指的是左侧只能匹配右侧一部分模式,这种情况叫做不完全解构。
js
let [x, y] = [1, 2, 3];
// x = 1; y = 2
2. 等号右侧不是数组
如果等于右侧不是数组会报错
js
let [foo] = 1; // 报错
let [foo] = true; // 报错
在执行时会先转化,其实只要数据结构具有Iterator接口,都能解构。
3. 嵌套解构
4. 默认值
解构时可以设置一个默认值,假如解构不成功时可以使用默认值,否则会报错。
只有解构时值为undefined,才会使用默认值。
js
let [ x = 1] = [];
// x = 1
这里结构变量x时,不存在但是给定默认值为1。
js
let [x, y = 1] = ['a'];
// x = 'a' y = 1
let [x = 1] = [undefined];
// x = 1
let [x = 1] = [null];
// x = 1
默认值也可以是表达式、其他变量
如果是表达式,只有使用时才会进行计算求值,否则不会主动计算。
js
function foo() {
return 2;
}
let [x = foo()] = [1]; // 1
let [x = foo()] = [undefined]; // 2
第一个解构时不会执行foo函数,因为解构能成功,第二个解构的值为undefined,所以会执行foo函数。
对于其他变量,必须保证变量是已经存在的。
js
let [x = 1, y = x] = []; // x = 1; y = 1
let [x = 1, y = x] = [2]; // x = 2; y = 2
let [x = 1, y = x] = [2, 3]; // x = 2; y = 3
let [x = y, y = 1] = []; // 报错
最后一个会报错是因为,y还没有声明呢,就直接使用了。
对象的解构赋值
对象的解构不必像数组那样按顺序排列,只要变量与属性名一致就能获取到值,对于取不到值的会被赋值为undefined。
js
let { foo, bar } = { foo: 'a', bar: 'b'};
// foo = 'a'; bar = 'b'
let { baz } = { foo: 'a', bar: 'b'};
// baz = undefined 不存在
对于属性名不一致可以使用冒号连接,冒号左边表示匹配的模式,这个模式要与解构的对象的属性名一致,冒号有侧就是自定义的变量名。
对象的解构本质就是通过冒号,左侧进行查找,右侧被真正赋值,只不过平时左侧、右侧名称一致,简写了而已。
js
let { foo: baz } = { foo: 'a', bar: 'b'};
// baz = 'a'
1. 嵌套解构
js
let obj = {
p: ['hello', {
y: 'world'
}]
}
let { p: [x, {y}]} = obj;
// x = 'hello'
// y = 'world'
这里的p
是匹配模式,并不是变量
2. 默认值
只有对象的属性值为undefined时,默认值才能生效。
js
let { x = 3 } = {};
// x = 3
对象的解构赋值可以很方便地将现有对象的方法赋值给某个变量。
js
let { log, sin, cos } = Math;
数组是特殊的对象,可以对数组进行对象属性结构,以数组的index。
js
let arr = [1, 2, 3];
let { 0: first, [arr.length - 1 ]:last } = arr;
// first = 1; last = 3
字符串的解构赋值
字符串也能解构,也解构成一个类似数组的对象。
不需要匹配的位置可以置空
js
let [a, b] = 'world';
// a = 'w'; b = 'o'
let [a, , b] = 'world';
// a = 'w'; b = 'r'
类似数组的对象都有一个length属性,所以可以对length属性进行解构
js
let { length: len } = 'hello';
// len = 5
数值和布尔类型解构
解构赋值时,等号右侧只要不是对象或者数组,就会先转为对象
js
let { toString: s } = 123;
s === Number.prototype.toString // true
函数参数的解构
函数的参数也能进行解构,也能使用默认值。
js
function add ({x, y}) {
return x + y;
}
add(1, 2); // 3
function boo({x = 1, y = 2}) {
return x + y;
}
add()
括号问题
正常解构是声明变量并且赋值,假如说变量已经声明成功,直接赋值,这时需要有所注意。
解构赋值虽然很方便,但是解析起来不容易。对于编译器来说,一个式子到底是模式还是表达式,没有办法从一开始就知道,必须解析到(或者解析不到)等号才知道,所以ES6规定,为了不导致歧义,不要使用圆括号。
js
let x;
{x} = {x:1};
// 报错
因为js会把{x}
当做一个代码块,而不是变量,从而发生语法错误。
1. 不能使用圆括号的情况
- 变量声明语句
会导致全部报错
js
let [ (a) ] = [1];
let { x: (c) } = {};
let ({ x: c }) = {};
let { (x: c) } = {};
let { (x): c } = {};
let { o: ({ p:p }) } = { o: { p: 2 } };
- 函数参数
函数的参数属于变量声明,不能使用圆括号
js
function f([(z)]) {
return z;
}
- 赋值语句的模式
js
({ p:a }) = { p: 42 };
([a]) = [5];
[({ p: a }), {x: c }] = [{}, {}];
不管是一部分模式还是全部模式都放在括号里面,都会报错。
2. 可以使用圆括号
赋值语句的非模式部分可以使用圆括号
js
[(b)] = [3];
({ p: (d) } = {});
[(parseInt.prop)] = [3];
用途
交换变量的值
js
let x = 1;
let y = 2;
[x, y] = [y, x];
// x = 2; y = 1
从函数返回多个值
函数只能返回一个值,如果真要返回多个只能放在数组和对象中,但是使用解构赋值就很方便。
js
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
js
// 参数是一组有次序的值
function f([x, y, z]) {}
f([1, 2, 3]);
js
// 参数是一组无次序的值
function f([x, y, z]) {}
f([z: 1, y: 2, x: 3]);
提取JSON数据
函数参数的默认值
在之前函数要是有默认值时,需要加判断,很不方便。
之前的
js
function foo(x) {
var y;
if(x != undefined) {
y = '默认值';
}
y = x;
}
foo();
现在直接使用解构赋值,添加默认值就可以
js
function foo(x = '默认值') {
}
foo();
变量Map解构
任何部署了Iterator接口的对象都可以使用for...of循环遍历,Map结构原生支持Iterator接口,因此配合变量的解构赋值获取键名和键值很方便。
js
var map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(`${key} is ${value}`);
}
// first is helllo
// second is world
获取键名
js
for(let [key] of map) {
// ...
}
获取键值
js
for (let [, value] of map) {
// ...
}
输入模块的指定方法
加载模块时,汪汪需要指定输入的方法,则使用解构赋值使输入语句清晰
js
// 现在
const { SourceMapConsumer, SourceNode } = require('source-map');
// 之前
const sourceMap = require('source-map');
const SourceMapConsumer = sourceMap.SourceMapConsumer();
const SourceNode = sourceMap.SourceNode();