ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
一、数组的解构赋值
//以前为变量赋值,只能直接指定值
let a=1;
let b=2;
let c=3;
//ES6 允许写成下面这样
let [a,b,c] = [1,2,3];
//上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值
本质上,这种写法属于‘模式匹配’,只要等号两边的模式相同,左边的变量就会被赋予对应的值,下面我们用例子来深入了解它:
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 // []
如果解构不成功,变量的值就等于undefined
let [foo] = [];
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
如果等号的右边不是数组(或者说不可便遍历的解构),那么将会报错。
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
//xxx is not iterable
解构赋值允许指定默认值
let [foo=true] = [];
foo; //true
let [x,y = 'b'] = ['a'];
x //'a'
y //'b'
let [x,y = 'b'] = ['a',undefined];
x //'a'
y //'b'
!:ES6内部使用严格相等运算符(===),所以只有当一个值严格等于undefined,默认值才会生效
let [x = 1] = [undefined]
x //1
let [x = 1] = [null]
x //null
如果默认值是一个表达式,这个表达式是惰性求值的,即只有在用到的时候,才会求值。
function f(){
console.log('aaa')
return 'aaa'
}
let [x = f()] = [1]
//这里的代码,x能取到值1,所以函数f()不会执行
let [x = f()] = [undefined]
//这里的代码,x取不到值,所以函数f()会执行
默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
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]=[];
//y is not defined
二、对象的解构赋值
let {bar,foo}={foo:'aaa',bar:'bbb'};
foo //'aaa'
bar //'bbb'
let {baz}={foo:'aaa',bar:'bbb'};
baz //undefined
对象的解构赋值,可以很方便地将现有对象的方法赋值到某个变量。
let {sin,cos,tan} = Math;
const {log} = console;
log('hello') //hellos
如果变量名与属性名不一致,必须写成下面这样。
let {foo:baz} = {foo:'aaa',bar:'bbb'};
baz //'aaa'
foo //foo is undefined
let obj={first:'hello',last:'world'};
let {first:f,last:l} = obj;
f //'hello'
l //'world'
这实际上说明,对象的解构赋值是下面形式的简写:
let {foo:foo,bar:bar} = { foo:'aaa',bar:'bbb' }
与数组一样,解构也可以用于嵌套结构的对象。
let obj = {
p:[
'Hello',
{y:'World'}
]
}
let {p,p:[x,{y}]} = obj;
p //['Hello',{y:'World'}]
x //'Hello'
y //'World'
const node = {
loc:{
start:{
line:1,
column:5
}
}
}
let {loc,loc:{start},loc:{start:{line}}} = node;
line //1
start //{line:1,column:5}
loc //{start:{line:1,column:5}}
下面是嵌套赋值的例子:
let obj = {};
let arr = [];
{foo:obj.prop,bar:arr[0]} = {foo:123,bar:true}
obj //{prop:123}
arr //[true]
对象的解构也可以指定默认值
var {x=3} = {};
x //3
var {x,y=5} = {x:1};
x //1
y //5
var {x:y=3}={};
y //3
var {x:y=3} = {x:5}
y //5
var { message:msg='Something went wrong'} = {}
msg //Something went wrong
默认值生效的条件是,对象的属性值严格等于undefined
var {x=3}={x:undefined};
x //3
var {x=3}={x:null}
x //null
由于数组本质是特殊的对象,因此可以对数组进行对象属性的结构
let arr = [1,2,3]
let {0:first,[arr.length-1]:last} = arr
first //1
last //3
三、字符串的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。
const [a,b,c,d,e] = 'hello';
a //'h'
b //'e'
c //'l'
d //'l'
e //'o'
let {length:len} = 'hello'
len //5
数值和布尔值的解构赋值
let {toString:a} = 123
a // f toString(){}
//123会先被转为Number对象
a ==== Number.prototype.toString //true
let {toString:s} = true
s // f toString(){}
//true会先被转为Boolean对象
s === Boolean.prototype.toString
四、解构的用途
1.交换变量的值
let x=1;
let y=2;
[x,y]=[y,x];
x //2
y //1
2.从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回,有了解构赋值,取出这些值就非常方便。
function example(){
return [1,2,3]
}
let [a,b,c] = example();
function example(){
return {
foo:1,
bar:2
}
}
let {foo,bar} = example();
3.函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
参数是一组有次序的值
function f ( [ x,y,z ]) { ... }
f([1,2,3]);
参数是一组无次序的值
function f({x,y,z}){ ... }
f({z:3,y:2,x:1})
4.提取JSON数据
let jsonData = {
id:42,
status:"OK",
data:[867,5309]
}
let {id, status, data:number} = jsonData;
console.log(id, status, number);
42,'OK',[867,5309]
5.函数参数的默认值
jQuery.ajax = function(url,{
async = true,
beforeSend = function(){},
cache = true,
complete = function(){},
crossDomain = false,
global = true,
// ...more config
}={}){
// ...do stuff
}
6.遍历Map结构
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
如果只想获取键名,或者只想获取键值,可以写成:
```javascript
for(let [key] of map){
//获取键名
}
for(let [,value] of map){
//获取键值
}
7.输入模块的制定方法
const {SourceMapConsumer,SourceNode} = require('source-map');