解构赋值是对赋值运算符的扩展,是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。
数组的解构赋值(Array)
基本
let [a,b,c] = [1,2,3];
console.log('a = '+a); // 1
console.log('b = '+b); // 2
console.log('c = '+c); // 3
可嵌套
let [foo, [[bar],baz]] = [1,[[2],3]];
console.log('foo = '+foo); // 1
console.log('bar = '+bar); // 2
console.log('baz = '+baz); // 3
可忽略
let [,,third] = ["foo","bar","baz"];
console.log('third = '+third); // baz
let [x, , y] = [1, 2, 3];
console.log('x = '+x); // 1
console.log('y = '+y); // 3
剩余运算符
let [head, ...tail] = [1,2,3,4];
console.log(head); // 1
console.log(tail); // [2,3,4]
let [x,y,...z] = ['a'];
console.log(x); // 'a'
console.log(y); // undefined
console.log(z); // []
// 如解构不成功,变量的值为undefined
如解构不成功,变量的值为undefined
let [ff] = [];
console.log(ff); // undefined
let [m,n] = [];
console.log(m,n); // undefined undefined
let bb = [];
bb = [99,88];
let [mm,nn] = bb;
console.log(mm,nn); // 99 88
不完全解构
等号左边的模式只匹配一部分的等号右边的数组,解构依然成功。
let [aaa = 1, bbb] = [];
console.log(aaa,bbb); // 1 undefined
let [aaaa, bbbb] = [1,2,3];
console.log(aaaa,bbbb); // 1 2
let [xx, [yy] ,zz] = [1,[2,3],4];
console.log(xx,yy,zz); // 1 2 4
如果等号的右边不是可遍历的结构,那么将会报错。
// let [k] = 1; // Uncaught TypeError: 1 is not iterable
// let [x] = false;
// let [h] = NaN;
// let [h] = undefined;
// let [h] = null;
// let [h] = { };
上边代码中的语句都会报错,因为等号右边的值或是转为对象后不具备Iterator接口(前5个表达式),或是本身不具备Iterator接口(最后表达式)。
只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值。
let [e,f,g] = new Set(['a','b','c']);
console.log(e); // a
function* fibs(){
let a = 0;
let b = 1;
while(true){
yield a;
[a,b] = [b,a+b]
}
}
let [first, second, third, fourth, fifth, sixth] = fibs();
console.log(sixth); // 5
解构默认值
let [ee = true] = [];
console.log('ee = '+ee); // ee = true
let [xxx,yyy='b'] = ['a'];
console.log('xxx = '+xxx); // xxx = a
console.log('yyy = '+yyy); // yyy = b
let [x1,y1='b'] = ['a',undefined];
console.log('x1 = '+x1); // x1 = a
console.log('y1 = '+y1); // y1 = b
ES6内部使用严格相等运算符(===)判断一个位置是否有值。所以,如一个数组成员不严格等于undefined,默认值是不生效的。
let [x2 = 1] = [undefined];
console.log('x2 = '+x2); // x2 = 1
let [x3 = 1] = [null];
console.log('x3 = '+x3); // x3 = null
如默认值是一个表达式,那该表达式是惰性求值的,即只有用到时才会求值。
function f1(){
console.log('kkkxxxhhh');
}
let [x4 = f1()] = [1];
console.log('x4 = '+x4); // x4 = 1
// 等价于下面代码
// let x4;
// if([1][0] === undefined){
// x4 = f1();
// }else{
// x4 = [1][0];
// }
默认值可引用解构赋值的其他变量,但该变量须已声明。
let [m1=1,n1=m1] = [];
console.log('m1 = '+m1+',n1 = '+n1);
// m1 = 1,n1 = 1
let [m2=1,n2=m2] = [2];
console.log('m2 = '+m2+',n2 = '+n2);
// m2 = 2,n2 = 2
let [m3=1,n3=m3] = [1,2];
console.log('m3 = '+m3+',n3 = '+n3);
// m3 = 1,n3 = 2
// let [m4=n4,n4=1] = [];
// console.log('m4 = '+m4+',n4 = '+n4);
// Uncaught ReferenceError: Cannot access 'n4' before initialization
对象模型的解构(Object)
对象解构与数组解构不同:
数组元素是按次序排列的,变量取值是由它的位置决定的;
而对象属性没有次序,变量必须与属性同名才能取到正确的值。
基本
let { k1, h1 } = { k1: 'aaa', h1: 'bbb' };
console.log('k1 = '+k1+',h1 = '+h1);
// k1 = aaa,h1 = bbb
let { baz : k } = { baz : 'ddd' };
console.log('k = '+k);
// k = ddd
let { k2, h2 } = { h2: 'bbb' , k2: 'aaa',};
console.log('k2 = '+k2+',h2 = '+h2);
// k2 = aaa,h2 = bbb
let { max } = { foo:'aaa',bar:'bbb', };
console.log('max = '+max);
// max = undefined
// 变量没有对应的同名属性,则取不到值,为undefined
如变量名与属性名不一致,必须写成如下样子:
var {k3:ba} = {k3:'aaa',h3:'bbb',};
console.log('ba = '+ba);
// ba = aaa
let obj = {first:'hello',last:'world'};
let {first:f0,last:l0,} = obj;
console.log('f0 = '+f0+',l0 = '+l0);
// f0 = hello,l0 = world
对象的解构赋值是下面形式的简写:
let {first:first,last:last,} = {first:'hello',last:'world'};
对象解构赋值内部机制是先找到同名属性,后再赋值给对应的变量。
真正赋值的是后者,而不是前者。
let {k4:bb0,} = {k4:'aaa',h4:'bbb'};
console.log('bb0 = '+bb0);
// bb0 = aaa
console.log('k4 = '+k4);
// Uncaught ReferenceError: k4 is not defined
可嵌套可忽略
let obj0 = {
p:[
'Hello',
{t:'World'}
]
};
let {p:[s,{t}]} = obj0;
console.log('s = '+s+',t = '+t);
// s = Hello,t = World
上面代码段,p是模式,不是变量,不会被赋值。若要p作为变量赋值,如下:
let obj0 = {
p:[
'Hello',
{t:'World'}
]
};
let {p,p:[s,{t}]} = obj0;
console.log(p);
// ["Hello", {t:'World'}]
console.log('s = '+s+',t = '+t);
// s = Hello,t = World
let node = {
loc:{
start:{
line:1,
column:5,
}
}
};
var { loc, loc:{ start }, loc:{ start:{line}, }, } = node;
console.log(line);
// 1
console.log(loc);
// { start: {line:1,column:5,} }
console.log(start);
// {line: 1, column: 5}
let obj1 = {};
let arr1 = [];
( { foo1:obj1.prop,bar1:arr1[0] } = { foo1:"dengjing",bar1:true, } );
console.log(obj1,arr1);
// {prop: "dengjing"} [true]
解构默认值
var {k5 = 3} = {};
console.log('k5 = '+k5);
// k5 = 3
var {k6,h6 = 5} = { k6 : 1 };
console.log('k6 = '+k6+',h6 = '+h6);
// k6 = 1,h6 = 5
var {k7: h7 = 3} = {};
console.log('h7 = '+h7);
// h7 = 3
var {k7: h7 = 3} = {k7:5};
console.log('h7 = '+h7);
// h7 = 5
var { message:msg = `Something went wrong!` } = {};
console.log('msg = '+msg);
// msg = Something went wrong!
默认值生效的条件是:对象的属性值严格等于undefined。
var {d = 3} = {x:undefined};
console.log('d = '+d);
// d = 3
var {f2 = 3} = {x:null};
console.log('f2 = '+f2);
// f2 = 3
如解构失败,变量值为undefined。
let {f3} = {bar:'kkxfh'};
console.log('f3 = '+f3);
// f3 = undefined
如解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错。
// let {foo: {r}} = {baz :`baz` } ;
// Uncaught TypeError: Cannot destructure property `r` of 'undefined' or 'null'.
下段代码报错:syntax error
let zzz0 ;
{zzz0} = {zzz0:1};
上代码会报错,因js引擎会将{x}理解成一个代码块,从而发生语法错误。
只有不将大括号写在行首,避免js将其解释为代码块,才能解决bug。
let zzz0 ;
({zzz0} = {zzz0:1});
console.log('zzz0 = '+zzz0);
// zzz0 = 1
对象的解构赋值可很方便地将现有对象的方法赋值到某个变量。
let {log,sin,cos} = Math;
数组本质是特殊的对象,因此可对数组进行对象属性的解构。
let arr = [1,2,3];
let {0:first0,[arr.length-1]:last0} = arr;
console.log('first0 = '+first0,'last0 = '+last0,);
// first0 = 1 last0 = 3
剩余运算符
let {a9, b9, ...rest} = {a9: 10, b9: 20, c: 30, d: 40};
console.log('a9 = '+a9,'b9 = '+b9,);
// a9 = 10 b9 = 20
console.log(rest);
// {c: 30, d: 40}
字符串的解构(String)
在数组的解构中,解构的目标若为可遍历对象,皆可进行解构赋值。
可遍历对象即实现 Iterator 接口的数据。
let [a09, b09, c09, d09, e09] = 'hello';
console.log('a09 = '+a09,);
// a09 = h
console.log('b09 = '+b09,);
// b09 = e
console.log('c09 = '+c09,);
// c09 = l
console.log('d09 = '+d09,);
// d09 = l
console.log('e09 = '+e09,);
// e09 = o
类似数组的对象都有一个length属性,因此可对这个属性进行解构赋值。
let {length:len} = 'hello';
console.log('len = '+len);
// len = 5
数值和布尔值的解构赋值
解构赋值时,如等号右边是数值和布尔值,则会先转为对象。
let {toString:s0} = 123;
console.log( s0 === Number . prototype.toString );
// true
console.log(s0);
// ƒ toString() { [native code] }
let {toString:s1} = true;
console.log( s1 === Boolean.prototype.toString );
// true
console.log(s1);
// ƒ toString() { [native code] }
上面代码段中,数值和布尔值的包装对象都有toString属性,so变量都能取到值。
解构赋值的规则是:只要等号右边的值不是对象或数组,就先将其转为对象。
let { prop:prop01 } = undefined;
// Uncaught TypeError: Cannot destructure property `prop` of 'undefined' or 'null'.
let { prop:prop02 } = null;
// Uncaught TypeError: Cannot destructure property `prop` of 'undefined' or 'null'.
上面代码段报错,undefined和null无法转为对象,so对其进行解构赋值时都会报错。
函数参数的解构赋值
function add([x,y]){
return x+y;
}
console.log( add([1,2]) );
// 3
函数add参数表面上是一个数组,但在传入参数那刻,数组参数就被解构成变量x和y。对于函数内部的代码来说,它们能感受到的参数就是x和y。
let arr2 = [ [1,2],[3,4] ].map( ([a,b])=>a+b );
console.log(arr2);
// [3, 7]
函数参数的解构也可以使用默认值。
// 为变量 x、y 指定默认值
function move({x=0,y=0}={}){
return [x,y];
}
console.log( move({x:3,y:8}) );
// [3, 8]
console.log( move({x:3,}) );
// [3, 0]
console.log( move({}) );
// [0, 0]
console.log( move() );
// [0, 0]
// 为函数move1的参数指定默认值
function move1({x,y}={x:0,y:0}){
return [x,y];
}
console.log( move1({x:3,y:8}) );
// [3, 8]
console.log( move1({x:3,}) );
// [3, undefined]
console.log( move1({}) );
// [undefined, undefined]
console.log( move1() );
// [0, 0]
undefined就会触发函数参数的默认值
let arr3 = [1 , undefined, 3].map( (x = 'yes' ) => x);
console.log( arr3 );
// [1, "yes", 3]
用途
交换变量值
let oo=1,pp=2;
[oo,pp] = [pp,oo];
console.log( 'oo = '+oo );
// oo = 2
console.log( 'pp = '+pp );
// pp = 1
从函数返回多个值
函数只能返回一个值,如要返回多个值,只能将它们放在数组或对象里返回。
// 返回一个数组
function example(){
return [1,2,3];
}
let [x5,h5,r5] = example();
console.log(x5,h5,r5);
// 1 2 3
// 返回一个对象
function example1(){
return {
x8:1,
h8:2,
r8:3,
};
}
let {x8,h8,r8,} = example1();
console.log(x8,h8,r8,);
// 1 2 3
函数参数的定义
// 参数是一组有次序的值
function ffff([x,y,z]){
// ...
}
ffff([1,2,3])
// 参数是一组无次序的值
function ffff1({x,y,z}){
// ...
}
ffff1({z:3,y:2,x:1})
提取JSON数据
解构赋值对提取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]
函数参数的默认值
指定参数的默认值,如此就避免了在函数体内部再写var foo = config.foo||'default.foo';这样的语句。
jQuery.ajax = function(url,{
async = true,
beforeSend = function(){},
cache = true,
complete = function(){},
crossDomain = false,
global = true,
// ... more config
}){
// ... do stuff
}
遍历Map结构
任何部署了Iterator接口的对象都可以用for...of...循环遍历。
Map结构原生支持Iterator接口,配合变量的解构赋值获取键名和键值就非常方便了。
var map = new Map();
map.set('fst','hello');
map.set('snd','world');
for(let [key,value] of map){
console.log(key+' is '+value);
}
// fst is hello
// snd is world
如只想获取键名或键值,可写成如下样:
// 获取键名
for(let [key] of map){
// ...
}
// 获取键值
for(let [,value] of map){
// ...
}
输入模块的指定方法
加载模块时,往往需要指定输入的方法。解构赋值使得输入语句非常清晰。
const { SourceMapConsumer, SourceNode } = require ('source-map');
console.log(SourceMapConsumer);
console.log(SourceNode);