1、数组解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
如:
let [a, b, c] = [1, 2, 3];
//a => 1, b => 2, c=> 3;
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。数组的元素是按次序排列的,变量的取值由它的位置决定。
注意
(1)等号右边必须是可遍历的,即具备 Iterator 接口。若等号右边不可遍历将会报错。如:
//以下代码会报错
let [foo1] = 1;
let [foo2] = false;
let [foo3] = NaN;
let [foo4] = undefined;
let [foo5] = null;
let [foo6] = {};
上述赋值语句都会报错。因为等号右边的值,要么转为对象后不具备 Iterator 接口(前五个表达式),要么本身就不具备 Iterator 接口(最后一个表达式)。
(2)解构失败,即等号右边少值。如果解构失败,变量的值就等于 undefined 。不完全解构,即等号左边少值。此时,忽略等号右边多余值。
(3)解构不等同于直接赋值。
//正常赋值
let a = 0;
let b = 1;
a = b;
b = a + b;
console.log(a+" "+ b); // a => 1, b => 2
//解构
let a = 0;
let b = 1;
[a, b] = [b, a + b];
console.log(a+" "+ b); // a => 1, b => 1
ES6 内部使用严格相等运算符( === )来判断一个位置是否有值。所以,如果一个数组成员不严格等于 undefined ,默认值是不会生效的。如:
let [a = 'a', b = 'b', c = a , d = a] = [1, undefined, null, ,2];
console.log(a+" "+ b + " " + c + " " + d);
// a => 1, b => b, c => null, d => 1
因为 null 和 空 不严格等于 undefined ,所以 c 和 d 的默认值不会生效,而是分别被赋予了 null 和 a 的值 1 。并且默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
2、对象的解构赋值
对象的数学没有次序,变量必须与属性同名,才能取到正确的值。
let {a, b, c ,d} = {a: 1, c: "c", b: "b" };
console.log(a+" "+ b + " " + c + " " + d);
//a => 1, b => b, c => c, d =>undefined
上述例子中, a 在左右两边的次序相同, b c 在左右两边的次序不一致,但是对取值完全没有影响。由于右边没有与 d 对应的同名属性,导致取不到值,最后等于 undefined 。
注意
(1)对象的解构赋值的内部机制,是先找到同名属性,然后再赋值给对应的变量。如
let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined
上述代码中, foo 是模式匹配, baz 才是变量。真正被赋值的是变量 baz ,而不是模式 foo 。也就是说真正被赋值的是后者,而不是前者。
当变量名与属性名同名是可省略变量名,但当变量名与属性名不一致时,必须写成 { foo : baz} 格式。
(2)对象解构与数组一样,可以使用嵌套结构,但是如果右边子对象所在的父附属性不存在,将会报错。因为等号左边对象的 foo 属性对应一个子对象 bar ,但是 foo 这时等于 undefined ,再取子属性将会报错;
let {foo: {bar}} = {bar:'bar'}; //报错Cannot match against 'undefined' or 'null'
let {bar} = {foo: {bar:'bar'}}; //bar => undefined
可以使用默认值,默认值生效的条件是对象的属性值严格等于 undefined 。
(3)若将一个已声明的变量用于解构赋值,必须要将整个解构赋值语句放在一个圆括号里,否则将报错。
let x;
({x} = {x : 1}); // 正确
{x} = {x : 1}; // 报错, Unexpected token =
这是因为, js 引擎会将 {x} 理解成一个代码块,从而产生的语法错误。
3、字符串对象的解构赋值
length 属性,因此还可以对这个属性解构赋值。
let [a , b, c] = 'get';// a => g, b => e, c => t
let {length : len} = 'get'; // len => 3
4、数值和布尔值的解构
解构赋值的规则是,只要等号右边的值不是对象或数组,就将其转为对象。
5、函数参数的解构赋值
函数的参数也可以使用解构赋值。
function add([x,y]){
console.log(x +" "+ y);
}
add(); //报错, Cannot read property 'Symbol(Symbol.iterator)' of undefined
add([1]) // x => 1, y => undefined
add([1,2]); // x => 1, y => 2
函数参数的解构也可以使用默认值。
function add({x = 0, y = 0} = {}){ // ={}是必须的,否则报错
console.log(x +" "+ y);
}
add(); // x => 0, y => 0
add({}); // x => 0, y => 0
add({x: 3}); // x => 3, y => 0
add({x: 3, y: 4}); // x => 3, y => 4
注意
function add({x,y} = {x : 0, y : 0}){
console.log(x +" "+ y);
}
add(); // x => 0, y => 0
add({}); // x => undefined, y => undefined
add({x: 3}); // x => 3, y => undefined
add({x: 3, y: 4}); // x => 3, y => 4
6、圆括号问题
以下三种解构赋值不得使用圆括号:
(1)变量声明语句,模式不能使用圆括号。
(2)函数参数,函数参数也属于变量声明,因此不能带有圆括号。
(3)赋值语句的模式部分不能使用圆括号。
// 全部报错
let [(a)] = [1]; // 第一种
function f([(z)]) { return z; } // 第二种
({ p: a }) = { p: 42 }; // 第三种
可以使用圆括号的情况只有一种:赋值语句的费模式部分可以使用圆括号:
[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
7、用途
(1)交换变量的值。 [x, y] = [y, x]
(2)从函数返回多个值。函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取这些值就非常方便。
(3)函数参数定义,解构赋值可以方便地将一组参数与变量名对应起来。
(4)提取JSON数据。
(5)函数参数的默认值,可以避免在函数体内进行参数是否赋值的判断。
(6)遍历Map解构。
for (let [key, value] of map) {
console.log(key + " is " + value);
}
(7)输入模块的指定方法。
const { SourceMapConsumer, SourceNode } = require("source-map");