什么是解構(gòu)賦值
解構(gòu)賦值允許你使用類似數(shù)組或?qū)ο笞置媪康恼Z(yǔ)法將數(shù)組和對(duì)象的屬性賦給各種變量伙狐。這種賦值語(yǔ)法極度簡(jiǎn)潔涮毫,同時(shí)還比傳統(tǒng)的屬性訪問(wèn)方法更為清晰瞬欧。
通常來(lái)說(shuō),我們?cè)L問(wèn)數(shù)組的元素的時(shí)候是這樣子的:
var first = someArray[0];
var second = someArray[1];
var third = someArray[2];
使用解構(gòu)賦值賦值則會(huì)變得比較簡(jiǎn)單并且直觀:
var [first, second, third] = someArray;
SpiderMonkey(Firefox的JavaScript引擎
)已經(jīng)支持解構(gòu)的大部分功能罢防,但是仍不健全艘虎。你可以通過(guò)bug 694100跟蹤解構(gòu)和其它ES6特性在SpiderMonkey中的支持情況。
數(shù)組和迭代器的解構(gòu)
以上是數(shù)組解構(gòu)的簡(jiǎn)單例子咒吐,語(yǔ)法的形式為:
// [變量1,變量2,...,變量N] = 數(shù)組
[ variable1, variable2, ..., variableN ] = array;
這將為variable1到variableN的變量賦予數(shù)組中相應(yīng)元素項(xiàng)的值野建。如果你想在賦值的同時(shí)聲明變量,可在賦值語(yǔ)句前加入var恬叹、let或const關(guān)鍵字候生,例如:
var [ variable1, variable2, ..., variableN ] = array;
let [ variable1, variable2, ..., variableN ] = array;
const [ variable1, variable2, ..., variableN ] = array;
事實(shí)上,用變量來(lái)描述并不恰當(dāng)绽昼,因?yàn)槟憧梢詫?duì)任意深度的嵌套數(shù)組進(jìn)行解構(gòu):
var [foo, [[bar], baz]] = [1, [[2], 3]];
console.log(foo); // => 1
console.log(bar); // => 2
console.log(baz); // => 3
此外唯鸭,你可以在對(duì)應(yīng)位留空來(lái)跳過(guò)被解構(gòu)數(shù)組中的某些元素:
var [,,third] = ["foo", "bar", "baz"];
console.log(third); // => "baz"
而且你還可以通過(guò)“不定參數(shù)”模式捕獲數(shù)組中的所有尾隨元素:
var [head, ...tail] = [1, 2, 3, 4];
console.log(head); // => 1
console.log(tail); // => [2, 3, 4]
當(dāng)訪問(wèn)空數(shù)組或越界訪問(wèn)數(shù)組時(shí),對(duì)其解構(gòu)與對(duì)其索引的行為一致硅确,最終得到的結(jié)果都是:undefined目溉。
console.log([][0]); // =>undefined
var [missing] = [];
console.log(missing); // => undefined
請(qǐng)注意,數(shù)組解構(gòu)賦值的模式同樣適用于任意迭代器:
function* fibs() { // 生成器函數(shù)
var a = 0;
var b = 1;
while (true) {
yield a; // yield: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/yield
[a, b] = [b, a + b];
}
}
var [first, second, third, fourth, fifth, sixth] = fibs();
console.log(sixth); // =>5
對(duì)象的解構(gòu)
通過(guò)解構(gòu)對(duì)象菱农,你可以把它的每個(gè)屬性與不同的變量綁定缭付,首先指定被綁定的屬性,然后緊跟一個(gè)要解構(gòu)的變量:
var robotA = { name: "Bender" };
var robotB = { name: "Flexo" };
var { name: nameA } = robotA;
var { name: nameB } = robotB;
console.log(nameA); // => "Bender"
console.log(nameB); // => "Flexo"
當(dāng)屬性名與變量名一致時(shí)循未,可以通過(guò)一種實(shí)用的句法簡(jiǎn)寫:
var { foo, bar } = { foo: "lorem", bar: "ipsum" };
console.log(foo); // => "lorem"
console.log(bar); // => "ipsum"
與數(shù)組解構(gòu)一樣陷猫,你可以隨意嵌套并進(jìn)一步組合對(duì)象解構(gòu):
var complicatedObj = {
arrayProp: [
"Zapp",
{ second: "Brannigan" }
]
};
var { arrayProp: [first, { second }] } = complicatedObj;
console.log(first); // => "Zapp"
console.log(second); // => "Brannigan"
當(dāng)你解構(gòu)一個(gè)未定義的屬性時(shí),得到的值為undefined:
var { missing } = {};
console.log(missing); // => undefined
請(qǐng)注意只厘,當(dāng)你解構(gòu)對(duì)象并賦值給變量時(shí)烙丛,如果你已經(jīng)聲明或不打算聲明這些變量(即賦值語(yǔ)句前沒(méi)有l(wèi)et、const或var關(guān)鍵字)羔味,你應(yīng)該注意這樣一個(gè)潛在的語(yǔ)法錯(cuò)誤:
{ blowUp } = { blowUp: 10 }; // Syntax error 語(yǔ)法錯(cuò)誤
為什么會(huì)出錯(cuò)河咽?這是因?yàn)镴avaScript語(yǔ)法通知解析引擎將任何以{開始的語(yǔ)句解析為一個(gè)塊語(yǔ)句(例如,{console}是一個(gè)合法塊語(yǔ)句
)赋元。解決方案是將整個(gè)表達(dá)式用一對(duì)小括號(hào)包裹:
({ safe } = {});
這樣就沒(méi)有語(yǔ)法錯(cuò)誤了忘蟹;
解構(gòu)值不是對(duì)象、數(shù)組或迭代器
當(dāng)你嘗試解構(gòu)null或undefined時(shí)搁凸,你會(huì)得到一個(gè)類型錯(cuò)誤:
var {blowUp} = null; // TypeError: null has no properties(null沒(méi)有屬性)
然而媚值,你可以解構(gòu)其它原始類型,例如:布爾值护糖、數(shù)值褥芒、字符串,但是你將得到undefined:
var {wtf} = NaN;
console.log(wtf); // undefined
你可能對(duì)此感到意外,但經(jīng)過(guò)進(jìn)一步審查你就會(huì)發(fā)現(xiàn)锰扶,原因其實(shí)非常簡(jiǎn)單献酗。當(dāng)使用對(duì)象賦值模式時(shí),被解構(gòu)的值[需要被強(qiáng)制轉(zhuǎn)換為對(duì)象坷牛。大多數(shù)類型都可以被轉(zhuǎn)換為對(duì)象罕偎,但null
和undefined
卻無(wú)法進(jìn)行轉(zhuǎn)換。當(dāng)使用數(shù)組賦值模式時(shí)京闰,被解構(gòu)的值一定要包含一個(gè)迭代器颜及。
默認(rèn)值
當(dāng)你要解構(gòu)的屬性未定義時(shí)你可以提供一個(gè)默認(rèn)值:
var [missing = true] = [];
console.log(missing); // =>true
var { message: msg = "Something went wrong" } = {};
console.log(msg); // => "Something went wrong"
var { x = 3 } = {};
console.log(x); //=> 3
解構(gòu)的實(shí)際應(yīng)用
函數(shù)參數(shù)定義
作為開發(fā)者,我們需要實(shí)現(xiàn)設(shè)計(jì)良好的API蹂楣,通常的做法是為函數(shù)設(shè)計(jì)一個(gè)對(duì)象作為參數(shù)俏站,然后將不同的實(shí)際參數(shù)作為對(duì)象屬性,以避免讓API使用者記住 多個(gè)參數(shù)的使用順序捐迫。我們可以使用解構(gòu)特性來(lái)避免這種問(wèn)題乾翔,當(dāng)我們想要引用它的其中一個(gè)屬性時(shí),大可不必反復(fù)使用這種單一參數(shù)對(duì)象施戴。
function removeBreakpoint({ url, line, column }) {
// ...
}
這是一段來(lái)自Firefox開發(fā)工具JavaScript調(diào)試器(同樣使用JavaScript實(shí)現(xiàn)
)的代碼片段,它看起來(lái)非常簡(jiǎn)潔萌丈,我們會(huì)發(fā)現(xiàn)這種代碼模式特別討喜赞哗。
配置對(duì)象參數(shù)
延伸一下之前的示例,我們同樣可以給需要解構(gòu)的對(duì)象屬性賦予默認(rèn)值辆雾。當(dāng)我們構(gòu)造一個(gè)提供配置的對(duì)象肪笋,并且需要這個(gè)對(duì)象的屬性攜帶默認(rèn)值時(shí),解構(gòu)特性就派上用場(chǎng)了度迂。舉個(gè)例子藤乙,jQuery的ajax函數(shù)使用一個(gè)配置對(duì)象作為它的第二參數(shù),我們可以這樣重寫函數(shù)定義:
jQuery.ajax = function (url, {
async = true,
beforeSend = noop,
cache = true,
complete = noop,
crossDomain = false,
global = true,
// ... 更多配置
}) {
// ... do stuff
};
如此一來(lái)惭墓,我們可以避免對(duì)配置對(duì)象的每個(gè)屬性都重復(fù)var foo = config.foo || theDefaultFoo;這樣的操作(可以設(shè)置默認(rèn)值
)坛梁。
與ES6迭代器協(xié)議協(xié)同使用
ECMAScript 6中定義了一個(gè)迭代器協(xié)議,我們?cè)凇?a target="_blank" rel="nofollow">深入淺出ES6(二):迭代器和for-of循環(huán)》中已經(jīng)詳細(xì)解析過(guò)腊凶。當(dāng)你迭代Maps(ES6標(biāo)準(zhǔn)庫(kù)中新加入的一種對(duì)象)后划咐,你可以得到一系列形如[key, value]
的鍵值對(duì),我們可將這些鍵值對(duì)解構(gòu)钧萍,更輕松地訪問(wèn)鍵和值:
var map = new Map();
map.set(window, "the global");
map.set(document, "the document");
for (var [key, value] of map) {
console.log(key + " is " + value);
}
// "[object Window] is the global"
// "[object HTMLDocument] is the document"
只遍歷鍵:
for (var [key] of map) {
// ...
}
或只遍歷值:
for (var [,value] of map) {
// ...
}
多重返回值
JavaScript語(yǔ)言中尚未整合多重返回值的特性褐缠,但是無(wú)須多此一舉,因?yàn)槟阕约壕涂梢苑祷匾粋€(gè)數(shù)組并將結(jié)果解構(gòu):
function returnMultipleValues() {
return [1, 2];
}
var [foo, bar] = returnMultipleValues();
或者风瘦,你可以用一個(gè)對(duì)象作為容器并為返回值命名:
function returnMultipleValues() {
return {
foo: 1,
bar: 2
};
}
var { foo, bar } = returnMultipleValues();
這兩個(gè)模式都比額外保存一個(gè)臨時(shí)變量要好得多队魏。
function returnMultipleValues() {
return {
foo: 1,
bar: 2
};
}
var temp = returnMultipleValues();
var foo = temp.foo;
var bar = temp.bar;
或者使用CPS變換:
function returnMultipleValues(k) {
k(1, 2);
}
returnMultipleValues((foo, bar) => ...);
使用解構(gòu)導(dǎo)入部分CommonJS模塊
你是否尚未使用ES6模塊?還用著CommonJS的模塊呢吧万搔!沒(méi)問(wèn)題胡桨,當(dāng)我們導(dǎo)入CommonJS模塊X時(shí)官帘,很可能在模塊X中導(dǎo)出了許多你根本沒(méi)打算用的函數(shù)。通過(guò)解構(gòu)登失,你可以顯式定義模塊的一部分來(lái)拆分使用遏佣,同時(shí)還不會(huì)污染你的命名空間:
const { SourceMapConsumer, SourceNode } = require("source-map");
(如果你使用ES6模塊,你一定知道在import聲明中有一個(gè)相似的語(yǔ)法揽浙。
)
解構(gòu)在許多獨(dú)立小場(chǎng)景中非常實(shí)用状婶。在Mozilla我們已經(jīng)積累了許多有關(guān)解構(gòu)的使用經(jīng)驗(yàn)。十年前馅巷,Lars Hansen在Opera中引入了JS解構(gòu)特性膛虫,Brendan Eich隨后就給Firefox也增加了相應(yīng)的支持,移植時(shí)版本為Firefox 2钓猬。所以我們可以肯定稍刀,漸漸地,你會(huì)在每天使用的語(yǔ)言中加入解構(gòu)這個(gè)新特性敞曹,它可以讓你的代碼變得更加精簡(jiǎn)整潔账月。
本文引自:http://www.infoq.com/cn/articles/es6-in-depth-destructuring/