ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构
数组的解构赋值
基本用法
ES6中对变量赋值可以写成下面的样式。
var [a,b,c] = [1,2,3];
//等同于下列三句
var a = 1;
var b = 2;
var c = 3;
本质上,这种写法属于‘模式匹配’,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
let [foo,[[bar],baz]] = [1,[2],3]];
console.log(foo);//1
console.log(bar);//2
console.log(baz);//3
let [,,third] = ['foo','bar','baz'];
console.log(third)//'baz'
let [head,...tail] = [1,2,3,4];
console.log(head);//1
console.log(tail);//[2,3,4]
//当解构不成功的时候,变量的值为undefined
let [foo] = []; //foo值为undefined
let [bar,foo] = [1]; //foo值为undefined
不完全解构
当等号左边的模式只匹配等号右边数组的一部分时,解构依然可以成功
let [x,y] = [1,2,3];//x值为1,y值为2
let [a,[b],d] = [1,[2,3],4];//a值为1,b值为2,d值为4
如果等号右边的不是可遍历的结构(数组、对象、Map和Set),那么将会报错。
let [foo] = 1;//报错
解构赋值不仅适用于var命令,也适用于let和const命令
let [v1,v2,...,vn] = array;
var [v1,v2,...,vn] = array;
const [v1,v2,...,vn] = array;
默认值
解构赋值允许指定默认值,当等号右边的值为undefined时,设置其值为默认值。这里需要主要,ES6内部使用严格相等运算符(===)判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined,默认值是不会生效的
var [foo = true] = [];//foo值为true
[x,y = 'b'] = ['a',undefined];//x = 'a',y = 'b'
var [x=1] = [null];//x=null
如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候才会求值。
function f(){
console.log('aaa');
}
let [x = f()] = [1];//函数f()不会执行
对象的解构赋值
解构不仅可以用于数组,还可以用于对象。但是对象的解构与数组的结构有一个重要的不同。数组的元素是按次序排列的,变量的取值是由它的位置决定的;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
var {bar,foo} = {foo:'aaa',bar:'bbb'};//bar的值为‘bbb’ foo的值为'aaa'
var {baz} = {foo:'aaa',bar:'bbb'};//baz变量没有对应的同名属性 导致其值为undefined
对象的解构赋值的内部机制是先找到同名属性,然后在赋给对应的变量。真正被赋值的是后者,而不是前者。
var {foo:baz} = {foo:'aaa',bar:'bbb'};
console.log(baz);//'aaa'
console.log(foo);//error:foo is not defined
注意:采用解构赋值的写法时,变量的声明和赋值是一体的。对于let和const而言,变量不能重新声明,所以一旦赋值的变量以前声明过,就会报错。不过var命令允许重新声明,所以这个错误只会在使用let和const命令时出现。
let foo;
let {foo} = {foo:1};//SyntaxError: redeclaration of let foo
let baz;
let {bar:baz} = {bar :1};//SyntaxError: redeclaration of let baz
与数组解构赋值相同,对象的解构也可以指定默认值。默认值的生效条件是:对象的属性值严格等于undefined
var {x = 3} = {}; //x = 3
var {x = 3} = {x : undefined};//x = 3
var {x = 3} = {x:null};//x = null
对象的解构赋值可以使用嵌套对象赋值
var {foo:{bar}} = {foo:{bar:'aaa'}};//bar的值为‘aaa’
如果将一个已经声明的变量用于解构赋值,JavaScript引擎会将{}理解成一个代码块,从而发生语法错误。可通过外加小括号避免将其解释为代码块。
{x} = {x:1};//SyntaxError: expected expression, got '='
//正确写法
({x} = {x:1})//x的值为1
对象的解构赋值可以很方便地将现有对象的方法赋值到某个变量。
//将Math对象的取对数、正弦、余弦三个方法赋值到对应变量
let {log,sin,cos} = Math;
console.log(sin(1));
字符串的解构赋值
由于字符串可以转换为一个类似数组的对象,字符串也可以解构赋值。类似数组的对象都有length属性也可以对这个属性解构赋值。
let [a,b,c,d,e] = 'hello';
let {length:len} = 'hello';
console.log(a);//'h'
console.log(len);//5
数值和布尔值的解构赋值
解构赋值时,如果等号右边是数值或布尔值,则会先转为对象。
let {toString:s} = 123;
s === Number.prototype.toString; //true
这里我在思考一个问题,数值的解构赋值如何能获得这个数值。 现在这个问题还没得到答案。如果有人知道,欢迎留言
由于undefined和null无法转换为对象,所以对他们进行解构赋值都会报错。
let {prop:x} = undefined;//TypeError
let {prop:y} = null;//TypeError
函数参数的解构赋值
函数的参数也可以解构赋值,并且也可以使用默认值。
function add([x,y]){
return x + y;
}
add([1,2]);//3
function move({x = 0, y = 0} = {}){
return [x,y];
}
move({x:3,y:8});//[3,8]
move({x:3});//[3,0]
这里还有一个比较混淆的地方,看下面的代码
function move({x,y} = {x:0,y:0}){
return [x,y];
}
move({x:3,y:8});//[3,8]
move({x:3});//[3,undefined]
move();//[0,0]
上面的代码是为函数move的参数指定默认值,而不是为变量x和y指定默认值,所以当传参的时候,不管参数的多少,参数的默认值是不起作用的,只有不传参数的时候,参数的默认值才有用。
用途
交换变量的值
以前我们交换变量通常需要再设置一个临时变量
var x = 1;
var y = 2;
var temp = 0;
temp = x;
x = y;
y = temp;
console.log(x + ' ' + y);//2 1
利用ES6的解构赋值方法,很容易实现两个变量值的交换
var x = 1;
var y = 2;
[x,y] = [y,x];
console.log(x + ' ' + y);//2 1
函数返回多个值
通常函数只能返回一个值,如果要返回多个值,只能将其放在数组或对象中返回。ES6的解构函数取出这些值非常方便
function example(){
return [1,2,3];
}
var [a,b,c] = example();
解构函数也可以很方便地将一组参数与变量名对应起来
//参数是一组有序的值
function f([x,y,z]){
//函数处理程序
}
f([1,2,3])
//参数是一组无序的值
function f({x,y,z}){
//函数处理程序
}
f({z:3,y:2,x:1})
通过解构赋值可以输入模块的指定方法
let {SourceMapConsumer,SourceNode} = require('source-map');