es6學(xué)習(xí)筆記 - 解構(gòu)賦值

什么是解構(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)寫(xiě):

    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ǔ)法通知解析引擎將任何以{開(kāi)始的語(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ì)象,但nullundefined 卻無(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ù)定義

作為開(kāi)發(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開(kāi)發(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ù),我們可以這樣重寫(xiě)函數(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/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容