解構賦值是對賦值運算符的擴展,是一種針對數組或者對象進行模式匹配,然後對其中的變量進行賦值。
數組的解構賦值(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);