解構(gòu)(Destructuring)
ES6引入了一個(gè)新的語法特性,叫做解構(gòu),這可能會(huì)和結(jié)構(gòu)化賦值的概念有點(diǎn)混淆。為了方便大家理解,考慮如下代碼:
function foo() {
return [1,2,3];
}
var tmp = foo(),
a = tmp[0], b = tmp[1], c = tmp[2];
console.log( a, b, c ); // 1 2 3
如你所見,我們手動(dòng)地把foo()返回的數(shù)組分別賦值給了a、b、c,這樣做我們必須(無可奈何地)引入變量tmp。
同樣地,我們也可以用對(duì)象來實(shí)現(xiàn):
function bar() {
return {
x: 4,
y: 5,
z: 6
};
}
var tmp = bar(),
x = tmp.x, y = tmp.y, z = tmp.z;
console.log( x, y, z ); // 4 5 6
屬性值tmp.x被賦值給了變量x,同樣地,y得到的是tmp.y,z是tmp.z。
手動(dòng)給數(shù)組的索引值或?qū)ο蟮膶傩灾蒂x值的方式被稱為結(jié)構(gòu)化賦值。ES6為此專門引入了新的語法,特別為數(shù)組解構(gòu)和對(duì)象解構(gòu)。有了這個(gè)語法我們就不需要前面代碼中的tmp變量了,可以讓代碼看起來更清晰??紤]:
var [ a, b, c ] = foo();
var { x: x, y: y, z: z } = bar();
console.log( a, b, c ); // 1 2 3
console.log( x, y, z ); // 4 5 6
在賦值的時(shí)候,你可能更習(xí)慣看到[a, b, c]這樣的語句位于=右邊,。
解構(gòu)對(duì)稱地翻轉(zhuǎn)了這個(gè)模式,所以在=賦值左邊的[a, b, c]被認(rèn)為是用來把右手邊數(shù)組里的值相應(yīng)地賦值給左邊的變量。
類似地,{ x: x, y: y, z: z }特指從bar()解構(gòu)對(duì)象的值從而賦值到多個(gè)變量中的這種方式。
對(duì)象屬性賦值模式
讓我們來深入前面代碼中的{ x: x, .. }語法。如果屬性的名字和你要聲明的變量名一致,你可以把它們簡寫為:
var { x, y, z } = bar();
console.log( x, y, z ); // 4 5 6
還不錯(cuò)吧?
但是{ x, ..}去掉的是x:還是: x部分呢?當(dāng)我們使用簡寫的時(shí)候,實(shí)際上是省略了x:。這似乎是個(gè)微不足道的細(xì)節(jié),但你馬上就會(huì)看到它的重要性。
如果可以簡寫,那我們有什么理由再去用啰嗦的方式呢?原因是這種長的形式允許我們用不同的變量名給屬性賦值,有時(shí)候還是挺有用的:
var { x: bam, y: baz, z: bap } = bar();
console.log( bam, baz, bap ); // 4 5 6
console.log( x, y, z ); // ReferenceError
這里有個(gè)很微妙卻非常重要的技巧來理解這個(gè)對(duì)象解構(gòu)形式的變體。為了說明這是個(gè)需要提防的陷阱,讓我們來看一般的對(duì)象賦值是怎么做的:
var X = 10, Y = 20;
var o = { a: X, b: Y };
console.log( o.a, o.b ); // 10 20
在{ a: X, b: Y }里,我們知道a是對(duì)象屬性,X是用來賦值的數(shù)據(jù)源。換句話說,這個(gè)語法的模式是target: source,說白了,就是property-alias: value。直覺告訴我們這和=賦值的用法是一樣的,就像target = source。
然而在使用對(duì)象解構(gòu)賦值的時(shí)候——也就是把看起來像對(duì)象一樣的{ .. }語法放在了=左邊的時(shí)候——其實(shí)我們是把target: source這個(gè)語法倒置了。
回想:
var { x: bam, y: baz, z: bap } = bar();
這里的語法模式是source: target(或value: variable-alias)。x: bam的意思是屬性x是數(shù)據(jù)源,bam是賦值的目標(biāo)變量。換句話說,對(duì)象語法是target <-- source,而對(duì)象解構(gòu)賦值是source --> target??吹竭@里的翻轉(zhuǎn)了?
還有另外一種方式來理解這個(gè)語法,可能更清楚一點(diǎn)??紤]:
var aa = 10, bb = 20;
var o = { x: aa, y: bb };
var { x: AA, y: BB } = o;
console.log( AA, BB ); // 10 20
在{ x: aa, y: bb }這行,x和y代表對(duì)象屬性。{ x: AA, y: BB }這行,,x和y也代表對(duì)象屬性。
回想之前我是如何斷定{ x, .. }丟掉了x:部分嗎?在那兩行代碼里,如果你去掉x:和y:兩部分,你會(huì)只剩下aa, bb和AA, BB,實(shí)際上——只是理論上,不是真的——是把aa賦值給BB以及bb賦值給BB。
這種對(duì)稱性可以幫助我們更好的理解為什么這種語法形式在ES6里被倒置了。
注意:我可能還是傾向于用{ AA: x , BB: y }來表示解構(gòu)語法,這樣可以和之前的target: source保持一直??上У氖俏业糜?xùn)練自己的大腦來習(xí)慣這個(gè)顛倒的形式,相信很多讀者也是。
不僅僅是聲明
到目前為之,我們都是用var來聲明解構(gòu)賦值(當(dāng)然,你也可以用let和const)。但解構(gòu)是一個(gè)通用的操作,并不僅僅是聲明。
考慮:
var a, b, c, x, y, z;
[a,b,c] = foo();
( { x, y, z } = bar() );
console.log( a, b, c ); // 1 2 3
console.log( x, y, z ); // 4 5 6
變量可以是聲明過的,解構(gòu)只做了賦值操作,正如我們所見。
注意:對(duì)于對(duì)象解構(gòu)形式,如果沒有var/let/const,就必須要用( )把整個(gè)賦值表達(dá)式包起來,不然表達(dá)式左手邊的第一個(gè){ .. }就會(huì)被當(dāng)成塊聲明,而不是對(duì)象。
事實(shí)上,賦值表達(dá)式(a, y等等)并不只能是變量標(biāo)識(shí)符。任何合法的賦值表達(dá)式都可以,例如:
var o = {};
[o.a, o.b, o.c] = foo();
( { x: o.x, y: o.y, z: o.z } = bar() );
console.log( o.a, o.b, o.c ); // 1 2 3
console.log( o.x, o.y, o.z ); // 4 5 6
你還可以用計(jì)算過的(變量)屬性表達(dá)式進(jìn)行解構(gòu)??紤]:
var which = "x",
o = {};
( { [which]: o[which] } = bar() );
console.log( o.x ); // 4
[which]:部分是計(jì)算出來的屬性,得到的是x——把對(duì)象中解構(gòu)出的屬性當(dāng)作賦值的源數(shù)據(jù)。o[which]部分就是一個(gè)正常的對(duì)象key引用,和o.x等價(jià),作為賦值目標(biāo)。
你可以用一般的賦值方法來創(chuàng)建對(duì)象關(guān)聯(lián)或轉(zhuǎn)型,例如:
var o1 = { a: 1, b: 2, c: 3 },
o2 = {};
( { a: o2.x, b: o2.y, c: o2.z } = o1 );
console.log( o2.x, o2.y, o2.z ); // 1 2 3
你也可以把對(duì)象映射成數(shù)組,比如:
var o1 = { a: 1, b: 2, c: 3 },
a2 = [];
( { a: a2[0], b: a2[1], c: a2[2] } = o1 );
console.log( a2 ); // [1,2,3]
或者反過來:
var a1 = [ 1, 2, 3 ],
o2 = {};
[ o2.a, o2.b, o2.c ] = a1;
console.log( o2.a, o2.b, o2.c ); // 1 2 3
也可以把數(shù)組重新排序變成另外一個(gè)數(shù)組:
var a1 = [ 1, 2, 3 ],
a2 = [];
[ a2[2], a2[0], a2[1] ] = a1;
console.log( a2 ); // [2,3,1]
甚至可以不用臨時(shí)變量就解決傳統(tǒng)的“交換變量值”問題:
var x = 10, y = 20;
[ y, x ] = [ x, y ];
console.log( x, y ); // 20 10
警告:小心:你不應(yīng)該把賦值和聲明混在一起,除非你有意讓這些賦值表達(dá)式同時(shí)做為聲明。不然的話,你會(huì)看到語法錯(cuò)誤。這也是為什么在前面的例子里我必須得先聲明var a2 = [],然后才能使用解構(gòu)賦值[ a2[0], .. ] = ..。嘗試var [ a2[0], .. ] = ..這樣做也沒有任何意義,因?yàn)?code>a2[0]都不是一個(gè)有效的聲明標(biāo)識(shí)符;顯然它也沒辦法創(chuàng)建var a2 = []聲明給我們用。
重復(fù)賦值
對(duì)象解構(gòu)語法允許一個(gè)源屬性(帶有任何值類型)多次出現(xiàn)。例如:
var { a: X, a: Y } = { a: 1 };
X; // 1
Y; // 1
這也意味著你可以解構(gòu)一個(gè)子對(duì)象/數(shù)組的屬性,同時(shí)拿到這個(gè)子對(duì)象/數(shù)組本身??紤]:
var { a: { x: X, x: Y }, a } = { a: { x: 1 } };
X; // 1
Y; // 1
a; // { x: 1 }
( { a: X, a: Y, a: [ Z ] } = { a: [ 1 ] } );
X.push( 2 );
Y[0] = 10;
X; // [10,2]
Y; // [10,2]
Z; // 1
關(guān)于解構(gòu),還是要提一句:雖然把所有解構(gòu)賦值放在一行代碼里看起來不錯(cuò),在我的示例里也是這也做的。但是為了可讀性起見,更好的辦法是采用多行賦值,并加上合理的縮進(jìn)——就像JSON或者是對(duì)象值一樣。
// 很難讀:
var { a: { b: [ c, d ], e: { f } }, g } = obj;
// 好一些:
var {
a: {
b: [ c, d ],
e: { f }
},
g
} = obj;
記住:**解構(gòu)賦值存在的意義不是節(jié)省你敲代碼的功夫,而是更好的可讀性。
解構(gòu)賦值表達(dá)式
帶有對(duì)象或數(shù)組解構(gòu)的賦值表達(dá)式,可以得等式右手邊的對(duì)象/數(shù)組完整的值??紤]:
var o = { a:1, b:2, c:3 },
a, b, c, p;
p = { a, b, c } = o;
console.log( a, b, c ); // 1 2 3
p === o; // true
在這段代碼中,p賦值給了對(duì)象o的引用,而不是a, b, c中的一個(gè)。下面的數(shù)組解構(gòu)也一樣:
var o = [1,2,3],
a, b, c, p;
p = [ a, b, c ] = o;
console.log( a, b, c ); // 1 2 3
p === o; // true
通過傳遞完整的對(duì)象/數(shù)組值,你可以把解構(gòu)賦值表達(dá)式串起來:
var o = { a:1, b:2, c:3 },
p = [4,5,6],
a, b, c, x, y, z;
( {a} = {b,c} = o );
[x,y] = [z] = p;
console.log( a, b, c ); // 1 2 3
console.log( x, y, z ); // 4 5 4
不多不少剛剛好
無論是數(shù)組解構(gòu)還是對(duì)象解構(gòu)賦值,你都不需要把所有出現(xiàn)的值都賦上:例如:
function foo() {
return [1,2,3];
}
function bar() {
return {
x: 4,
y: 5,
z: 6
};
}
var [,b] = foo();
var { x, z } = bar();
console.log( b, x, z ); // 2 4 6
foo()返回的1和3被拋棄了,bar()返回的5也是。
類似地,如果你想賦值的變量比出現(xiàn)的變量還多,那么你會(huì)優(yōu)雅地回落到備用的undefined,正如你所期待的:
var [,,c,d] = foo();
var { w, z } = bar();
console.log( c, z ); // 3 6
console.log( d, w ); // undefined undefined
這個(gè)行為也相應(yīng)地遵從前面提到過的“找不到undefined”原則。
在本章的前面我們已經(jīng)研究過了操作符...,也了解了它有時(shí)可以用來把數(shù)組里的值展開成單獨(dú)的值,有時(shí)可以用它做相反的事情:把一組值聚合成一個(gè)數(shù)組。
除了在函數(shù)聲明里可以聚合參數(shù),...在解構(gòu)賦值時(shí)也可以做同樣的事情。為了清楚地說明,我們回憶一下早前的一段代碼:
var a = [2,3,4];
var b = [ 1, ...a, 5 ];
console.log( b ); // [1,2,3,4,5]
這里我們可以看到...a展開了a,因?yàn)樗挥跀?shù)組[ .. ]值中間。如果...a出現(xiàn)在數(shù)組解構(gòu)里,它則會(huì)表現(xiàn)出聚合:
var a = [2,3,4];
var [ b, ...c ] = a;
console.log( b, c ); // 2 [3,4]
var [ .. ] = a把a解構(gòu)成[ .. ]里面的結(jié)構(gòu)。第一部分叫做b對(duì)應(yīng)的是a的第一個(gè)值2。然后...c聚合其余的值(3和4)放到叫做c的數(shù)組里。
注意:我們見過...在數(shù)組里是如何工作的,但對(duì)象呢?它并不是一個(gè)ES6的特性,不過可以參考第八章里討論的“beyond ES6”特性,...也可以用來聚合或者展開對(duì)象。
默認(rèn)值賦值
這兩種解構(gòu)賦值形式都支持使用默認(rèn)值,和函數(shù)參數(shù)默認(rèn)值一樣,用=就可以了
考慮:
var [ a = 3, b = 6, c = 9, d = 12 ] = foo();
var { x = 5, y = 10, z = 15, w = 20 } = bar();
console.log( a, b, c, d ); // 1 2 3 12
console.log( x, y, z, w ); // 4 5 6 20
你還可以同時(shí)使用默認(rèn)值賦值和前面提到的選擇性賦值表達(dá)式,例如:
var { x, y, z, w: WW = 20 } = bar();
console.log( x, y, z, WW ); // 4 5 6 20
在對(duì)象或數(shù)組解構(gòu)賦值使用默認(rèn)值的時(shí)候,小心不要把自己繞暈(還有讀你代碼的人)。你可能會(huì)寫出非常難懂的代碼:
var x = 200, y = 300, z = 100;
var o1 = { x: { y: 42 }, z: { y: z } };
( { y: x = { y: y } } = o1 );
( { z: y = { y: z } } = o1 );
( { x: z = { y: x } } = o1 );
你能得出最后x, y和z最后的值是什么嗎?我可以想象這需要花點(diǎn)時(shí)間來想明白。我來揭開謎底:
console.log( x.y, y.y, z.y ); // 300 100 42
這里最大的啟發(fā)就是:解構(gòu)很棒很有用,但如果濫用它的話,可能會(huì)很傷腦筋了。
嵌套解構(gòu)
如果解構(gòu)的值有嵌套的對(duì)象或數(shù)組,你也可以解構(gòu)這些嵌套的值:
var a1 = [ 1, [2, 3, 4], 5 ];
var o1 = { x: { y: { z: 6 } } };
var [ a, [ b, c, d ], e ] = a1;
var { x: { y: { z: w } } } = o1;
console.log( a, b, c, d, e ); // 1 2 3 4 5
console.log( w ); // 6
嵌套解構(gòu)可以作為平鋪對(duì)象命名空間平鋪的簡單方式,例如:
var App = {
model: {
User: function(){ .. }
}
};
// instead of:
// var User = App.model.User;
var { model: { User } } = App;
參數(shù)解構(gòu)
你能找到下面這段代碼里的賦值嗎?
function foo(x) {
console.log( x );
}
foo( 42 );
這個(gè)賦值有點(diǎn)隱蔽:當(dāng)foo(42)被調(diào)用的時(shí)候,42(參數(shù))被賦值給了x。如果參數(shù)對(duì)是一個(gè)賦值,它就說明了賦值可以被解構(gòu),對(duì)吧?當(dāng)然啦!
考慮這種數(shù)組解構(gòu)參數(shù):
function foo( [ x, y ] ) {
console.log( x, y );
}
foo( [ 1, 2 ] ); // 1 2
foo( [ 1 ] ); // 1 undefined
foo( [] ); // undefined undefined
對(duì)象解構(gòu)參數(shù)也可以:
function foo( { x, y } ) {
console.log( x, y );
}
foo( { y: 1, x: 2 } ); // 2 1
foo( { y: 42 } ); // undefined 42
foo( {} ); // undefined undefined
這種方式和命名參數(shù)很類似(被要求加入JS特性很久了?。?,在這里對(duì)象上的屬性映射為相同名字的解構(gòu)參數(shù)。這也意味著我們也順便拿到了可選參數(shù)(在任何位置),如你所見到的,缺失的參數(shù)x和我們預(yù)想的行為一致。
當(dāng)然,前面所討論的各種解構(gòu)形式都適用于參數(shù)解構(gòu),包括嵌套解構(gòu)、默認(rèn)值等等。解構(gòu)也適用于其他的ES6函數(shù)參數(shù)行為,比如參數(shù)默認(rèn)值和展開/聚合參數(shù)。
參考下面的一些簡要說明(當(dāng)然沒辦法窮盡所有可能):
function f1([ x=2, y=3, z ]) { .. }
function f2([ x, y, ...z], w) { .. }
function f3([ x, y, ...z], ...w) { .. }
function f4({ x: X, y }) { .. }
function f5({ x: X = 10, y = 20 }) { .. }
function f6({ x = 10 } = {}, { y } = { y: 10 }) { .. }
我們從上面的例子中拿一個(gè)來詳細(xì)說明:
function f3([ x, y, ...z], ...w) {
console.log( x, y, z, w );
}
f3( [] ); // undefined undefined [] []
f3( [1,2,3,4], 5, 6 ); // 1 2 [3,4] [5,6]
這里有兩個(gè)...操作符,它們都在聚合數(shù)組里的值(z和w),然后...z聚合了第一個(gè)數(shù)組參數(shù)剩下的參數(shù),而...w聚合了主參數(shù)除了第一個(gè)參數(shù)以外的參數(shù)。
解構(gòu)默認(rèn)值 + 參數(shù)默認(rèn)值
有一個(gè)很微妙的地方你需要注意——解構(gòu)默認(rèn)值和函數(shù)參數(shù)默認(rèn)值的行為是有不同之處的。例如:
function f6({ x = 10 } = {}, { y } = { y: 10 }) {
console.log( x, y );
}
f6(); // 10 10
首先,看起來好像我們給x和y都設(shè)置了默認(rèn)值10,只是用了兩種不同的方式。然而,這兩種不同的方式在特定的場景下會(huì)有不同的行為,并且非常微妙。
Consider:
f6( {}, {} ); // 10 undefined
等等,為什么會(huì)這樣?很明顯,命名為x的參數(shù)在第一個(gè)參數(shù)沒有相同名字的屬性時(shí),會(huì)使用默認(rèn)值10。
但為什么y是undefined呢?對(duì)象值{ y: 10 }是一個(gè)函數(shù)參數(shù)默認(rèn)值,而不是解構(gòu)默認(rèn)值。因此,它只會(huì)在沒傳第二個(gè)參數(shù)的時(shí)候起作用,或是undefined的時(shí)候。
在前面的代碼中,我們傳了第二過參數(shù)({}),所以默認(rèn)的{ y: 10 }并沒有起作用,而{ y }在嘗試解構(gòu)空對(duì)象{}。
現(xiàn)在,比較一下{ y } = { y: 10 }和{ x = 10 } = {}。
對(duì)于x這種使用方式來說,如果沒有傳第一個(gè)函數(shù)參數(shù)或者是undefined,就會(huì)使用空對(duì)象{}。然后,無論什么第一個(gè)傳進(jìn)來的是什么值——無論是默認(rèn)值{}還是傳進(jìn)去的任何東西——會(huì)用表達(dá)式{ x = 10 }來解構(gòu),它會(huì)檢查屬性x是否存在,如果沒有(或者是undefined),參數(shù)x就會(huì)使用默認(rèn)值10。
深呼吸。把前面幾段反復(fù)讀幾次,然后我們?cè)賮砜聪旅娴拇a:
function f6({ x = 10 } = {}, { y } = { y: 10 }) {
console.log( x, y );
}
f6(); // 10 10
f6( undefined, undefined ); // 10 10
f6( {}, undefined ); // 10 10
f6( {}, {} ); // 10 undefined
f6( undefined, {} ); // 10 undefined
f6( { x: 2 }, { y: 3 } ); // 2 3
可能大多數(shù)人會(huì)認(rèn)為和y的行為相比,參數(shù)x的行為會(huì)更合理一些。因此,理解這兩種形式為什么會(huì)有差異,以及差異在哪里就顯得很重要。
如果你還是有點(diǎn)懵就把上面的內(nèi)容再看一遍,自己也玩一下。未來的你會(huì)感激你現(xiàn)在花時(shí)間把這個(gè)東西理解透了的。
嵌套默認(rèn)值:解構(gòu)和重組
這種方式乍看上去很難懂,但最近很流行這樣做,用嵌套的對(duì)象屬性來設(shè)默認(rèn)值:和重組(我這么叫)一起使用對(duì)象解構(gòu)。
考慮一組嵌套對(duì)象解構(gòu)默認(rèn)值,如下:
// 見http://es-discourse.com/t/partial-default-arguments/120/7
var defaults = {
options: {
remove: true,
enable: false,
instance: {}
},
log: {
warn: true,
error: true
}
};
假如你有個(gè)對(duì)象叫config,部分符合上面的結(jié)構(gòu),但可能不是全部;你想要把所有缺失的部分設(shè)置默認(rèn)值,但又不覆蓋已有的設(shè)置:
var config = {
options: {
remove: false,
instance: null
}
};
你可以像過去一樣手動(dòng)實(shí)現(xiàn)它:
config.options = config.options || {};
config.options.remove = (config.options.remove !== undefined) ?
config.options.remove : defaults.options.remove;
config.options.enable = (config.options.enable !== undefined) ?
config.options.enable : defaults.options.enable;
...
Eww。
也有些人傾向于用賦值-覆蓋方式來做。你可能會(huì)被ES6的Object.assign(..)方法(見第六章)先來克隆defaults的屬性然后再用config的值來覆蓋,像下面這樣:
config = Object.assign( {}, defaults, config );
看起來好一點(diǎn)吧?但這里有個(gè)嚴(yán)重的問題!Object.assign(..)是淺賦值,也就是說當(dāng)它復(fù)制defaults.options的時(shí)候,只賦值了對(duì)象引用,沒有進(jìn)一步把對(duì)象屬性克隆到config.options對(duì)象。Object.assign(..)需要在對(duì)象屬性樹的每一層(像遞歸一樣)都調(diào)用一下才能實(shí)現(xiàn)你想要的深度克隆。
注意:很多JS的方法庫/框架都提供他們自己的深度克隆對(duì)象方法,但是他們的做法不在我們的討論范圍之內(nèi)。
我們來看一下ES6帶有默認(rèn)值的對(duì)象解構(gòu)能不能解決這個(gè)麻煩:
config.options = config.options || {};
config.log = config.log || {};
({
options: {
remove: config.options.remove = defaults.options.remove,
enable: config.options.enable = defaults.options.enable,
instance: config.options.instance = defaults.options.instance
} = {},
log: {
warn: config.log.warn = defaults.log.warn,
error: config.log.error = defaults.log.error
} = {}
} = config);
表面上并沒有Object.assign(..)這樣虛假的承諾那么好看(但它僅僅是淺賦值),但我覺得比起手動(dòng)賦值還是好一點(diǎn)。盡管看起來還是又臭又長。
前面代碼中的方式可以工作,因?yàn)槲襤ack了解構(gòu)和默認(rèn)值的機(jī)制,來檢查屬性是不是=== undefined然后決定是否賦值。我在解構(gòu)config的同時(shí)又把解構(gòu)出來的值重新賦給了config,通過config.options.enable賦值引用(見代碼最后的= config)。
然而還是太麻煩了。我們來看看還可以怎么改進(jìn)。
下面這種做法是我們認(rèn)為做好的,如果你清楚地知道你在解構(gòu)的各種屬性都有唯一的命名。如果不是這樣的話,你也可以這么做,但看起來就沒那么好看了——你不得不分階段的解構(gòu),或者給那些重復(fù)的命名創(chuàng)建臨時(shí)唯一變量。
如果我們完全解構(gòu)所有頂層變量,我們隨即就可以重組還原這個(gè)嵌套的對(duì)象解構(gòu)。
但是所有這些臨時(shí)變量就會(huì)縈繞在周圍污染作用域。所以,我們還是用塊作用域{ }的方式把這部分包起來(見本章前面的“塊作用域”部分)。
// 把`defaults`并入`config`
{
// 解構(gòu)(用默認(rèn)值賦值方式)
let {
options: {
remove = defaults.options.remove,
enable = defaults.options.enable,
instance = defaults.options.instance
} = {},
log: {
warn = defaults.log.warn,
error = defaults.log.error
} = {}
} = config;
// 重組
config = {
options: { remove, enable, instance },
log: { warn, error }
};
}
看起來好一點(diǎn)了吧?
注意:你也可以用立即執(zhí)行箭頭函數(shù)來替代塊作用域的{ }和let聲明。這樣的話,解構(gòu)賦值/默認(rèn)值需要在參數(shù)列表里,以及重組的結(jié)果需要在函數(shù)體中返回(return)。
在重組部分的{ warn, error }可能對(duì)你來說有點(diǎn)眼生,這種形式叫做“簡明屬性”,我們會(huì)在下一節(jié)詳細(xì)講它。
該系列文章翻譯自Kyle Simpson的《You don't know about Javascript》,本章原文在此。