数组的结构赋值
1.基本用法
ES6允许按照一定模式从数组和对象中提取值,然后对变量进行赋值,被称为 解构赋值
曾经,我们是这样赋值的:
let a = 1;
let b = 2;
let c = 3;
现在,我们升级了,可以这么写!
let [a,b,c] = [1,2,3];
↑ 这段代码表示我们可以从数组中提取值,给对应位置变量赋值。
本质上这种写法属于 匹配模式,只要等号两边的模式相同,左边的变量就会被赋予对应位置的值,下面举一些栗子:
- 完全解构栗子
let [a,[b],c] = [1,[2],3];
console.log(a,b,c); // 1 2 3
let [x,,y] = [1,2,3];
console.log(x,y); // 1 3
let [head,...tail] = [1,2,3,4];
console.log(head,tail); // 1 [2,3,4]
let [x,y,...z] = ['a'];
console.log(x,y,z); // 'a' undefined []
// 如果解构不成功,变量的值就等于 undefined
- 不完全解构栗子(等号左边的模式职匹配等号右边数组的一部分,但是依然可以解构成功)
let [x,y] = [1,2,3];
console.log(x,y); // 1 2
let [a,[b],c] = [1,[2,3],4];
console.log(a,b,c); // 1 2 4
- 不能解构栗子
let [a] = 1;
let [a] = false;
let [a] = NaN;
let [a] = undefined;
let [a] = null;
let [a] = {};
// 上面的语句都会报错 因为等号右边的值本身不具备Iterator接口或转为对象以后不具备Iterator接口
对于Set解构,也可以使用数组的解构赋值!
let [x,y,z] = new Set('a','b','c');
console.log(x); // a
2.默认值
看一段代码
let [a = true] = [];
console.log(a); // true
// 即使在等号右边找不到对应的值,也能输出默认值
let [x, y = 'b'] = ['a']; // x = 'a' y = 'b'
再看一段代码
let [x = 1] = [undefined];
let [y = 1] = [null];
console.log(x,y); // 1 null
这是为什么呢?因为ES6内部严格相等符号 (===) 判断一个位置是否有值,所以如果一个数组成员不严格等于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] = []; // ReferenceError
对象的解构赋值
解构赋值不仅可以用于数组,还可以用于对象。
let {foo,bar} = {foo:'aaa',bar:'bbb'};
console.log(foo,bar); // 'aaa' 'bbb'
对象的结构与数组有一个重要的不同。数组元素是按照次序排列的,变量值由它的位置决定,而对象的属性没有次序,变量必须与属性同名才能取到正确的值。
let {baz} = {foo:'a',bar:'b'};
conosle.log(baz); // undefined
let {foo:baz} = {foo:'a',bar:'b'}
console.log(baz); // a
let obj = {a:'a',b:'b'};
let {a:aa,b:bb} = obj;
console.log(aa,bb); // 'a' 'b'
实际上说明对象的结构赋值是下面形式的简写!
let {a:a,b:b} = {a:'aaa',b:'bbb'};
所以对象的解构赋值内部机制是找到同名属性,再赋值给对应的变量。其实真正被赋值的是后者。
字符串的解构赋值
字符串也可以解构赋值。这是因为字符串被转换成了一个类似数组的对象。
const [a,b,c,d,e] = 'hello';
console.log(a,b,c,d,e); // h e l l o
类似数组的对象都有一个length属性,因此还可以对这个属性进行解构赋值
let {length:len} = 'hello';
console.log(len); // 5
数值和布尔类型的结构赋值
结构赋值时,如果等号右边是数值或布尔值,则会先转为对象。
let {toString:s} = 123;
s === Number.prototype.toString // true
let {toString:s} = true;
s === Boolean.prototype.toString // true
上面的代码中数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值时都会报错。
let {prop : x} = undefined; // TypeError
let {prop : y} = null; // TypeError
函数参数的结构赋值
function add([x,y]){
return x + y;
}
add([1,2]); // 3
圆括号问题
结构赋值虽然很方便,但是解析起来并不容易。对于编译器来说,一个式子到底是模式还是表达式,没有办法从一开始就知道,必须解析到(或者解析不到)等号才知道。
由此带来的问题是,如果模式中出现圆括号该怎么处理呢!ES6的规则是:只要有可能导致结构的歧义,就不得使用圆括号。
用途
1.交换变量的值
let x = 1;
let y = 2;
[x,y] = [y,x];
上面的代码交换变量x和y的值,这样的写法非常清晰!!
2.从函数返回多个值
// 返回一个数组
function fn() {
return [1,2,3];
}
let [a,b,c] = fn();
// 返回一个对象
function fn() {
return {
foo:1,
bar:2
}
}
let {foo,bar} = fn();
3.函数参数的定义
// 参数是一组有次序的值
function fn([x,y,z]){
...
}
fn([1,2,3]);
// 参数是一组无次序的值
function fn({x,y,z}){
...
}
fn({z:1,x:3,y:})
4.提取json数据
let jsonData = {
id: 42,
status: 'OK',
data: [1,2]
}
let {id,status,data:number} = jsonData;
console.log(id,status,data:number); // 42 OK [1,2]
5.遍历 map 结构
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 hello
// second is world
6.输入模块的制定方法
加载模块时,往往需要指定输入的方法,结构赋值使得输入语句非常清晰 ~
const {aFun,bFun} = require('c');