1. 程式人生 > >深入淺出ES6(六):解構 Destructuring

深入淺出ES6(六):解構 Destructuring

什麼是解構賦值?

解構賦值允許你使用類似陣列或物件字面量的語法將陣列和物件的屬性賦給各種變數。這種賦值語法極度簡潔,同時還比傳統的屬性訪問方法更為清晰。

通常來說,你很可能這樣訪問陣列中的前三個元素:

    var first = someArray[0];
    var second = someArray[1];
    var third = someArray[2];

如果使用解構賦值的特性,將會使等效的程式碼變得更加簡潔並且可讀性更高:


    var [first, second, third] = someArray;

SpiderMonkey(Firefox的JavaScript引擎)已經支援解構的大部分功能,但是仍不健全。你可以通過

bug 694100跟蹤解構和其它ES6特性在SpiderMonkey中的支援情況。

陣列與迭代器的解構

以上是陣列解構賦值的一個簡單示例,其語法的一般形式為:


    [ variable1, variable2, ..., variableN ] = array;

這將為variable1到variableN的變數賦予陣列中相應元素項的值。如果你想在賦值的同時宣告變數,可在賦值語句前加入varletconst關鍵字,例如:


    var [ variable1, variable2, ..., variableN ] = array;
    let [ variable1, variable2,
..., variableN ] = array; const [ variable1, variable2, ..., variableN ] = array;

事實上,用變數來描述並不恰當,因為你可以對任意深度的巢狀陣列進行解構:


    var [foo, [[bar], baz]] = [1, [[2], 3]];
    console.log(foo);
    // 1
    console.log(bar);
    // 2
    console.log(baz);
    // 3

此外,你可以在對應位留空來跳過被解構陣列中的某些元素:


    var [,,third]
= ["foo", "bar", "baz"]; console.log(third); // "baz"

而且你還可以通過“不定引數”模式捕獲陣列中的所有尾隨元素:


    var [head, ...tail] = [1, 2, 3, 4];
    console.log(tail);
    // [2, 3, 4]

當訪問空陣列或越界訪問陣列時,對其解構與對其索引的行為一致,最終得到的結果都是:undefined

    console.log([][0]);
    // undefined
    var [missing] = [];
    console.log(missing);
    // undefined

請注意,陣列解構賦值的模式同樣適用於任意迭代器:

    function* fibs() {
      var a = 0;
      var b = 1;
      while (true) {
        yield a;
        [a, b] = [b, a + b];
      }
    }
    var [first, second, third, fourth, fifth, sixth] = fibs();
    console.log(sixth);
    // 5

物件的解構

通過解構物件,你可以把它的每個屬性與不同的變數繫結,首先指定被繫結的屬性,然後緊跟一個要解構的變數。

    var robotA = { name: "Bender" };
    var robotB = { name: "Flexo" };
    var { name: nameA } = robotA;
    var { name: nameB } = robotB;
    console.log(nameA);
    // "Bender"
    console.log(nameB);
    // "Flexo"

當屬性名與變數名一致時,可以通過一種實用的句法簡寫:


    var { foo, bar } = { foo: "lorem", bar: "ipsum" };
    console.log(foo);
    // "lorem"
    console.log(bar);
    // "ipsum"

與陣列解構一樣,你可以隨意巢狀並進一步組合物件解構:

    var complicatedObj = {
      arrayProp: [
        "Zapp",
        { second: "Brannigan" }
      ]
    };
    var { arrayProp: [first, { second }] } = complicatedObj;
    console.log(first);
    // "Zapp"
    console.log(second);
    // "Brannigan"

當你解構一個未定義的屬性時,得到的值為undefined

    var { missing } = {};
    console.log(missing);
    // undefined

請注意,當你解構物件並賦值給變數時,如果你已經宣告或不打算宣告這些變數(亦即賦值語句前沒有letconstvar關鍵字),你應該注意這樣一個潛在的語法錯誤:

    { blowUp } = { blowUp: 10 };
    // Syntax error 語法錯誤

為什麼會出錯?這是因為JavaScript語法通知解析引擎將任何以{開始的語句解析為一個塊語句(例如,{console}是一個合法塊語句)。解決方案是將整個表示式用一對小括號包裹:

    ({ safe } = {});
    // No errors 沒有語法錯誤

解構值不是物件、陣列或迭代器

當你嘗試解構nullundefined時,你會得到一個型別錯誤:

    var {blowUp} = null;
    // TypeError: null has no properties(null沒有屬性)

然而,你可以解構其它原始型別,例如:布林值數值字串,但是你將得到undefined

    var {wtf} = NaN;
    console.log(wtf);
    // undefined

你可能對此感到意外,但經過進一步審查你就會發現,原因其實非常簡單。當使用物件賦值模式時,被解構的值需要被強制轉換為物件。大多數型別都可以被轉換為物件,但nullundefined卻無法進行轉換。當使用陣列賦值模式時,被解構的值一定要包含一個迭代器

預設值

當你要解構的屬性未定義時你可以提供一個預設值:

    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

(譯者按:Firefox目前只實現了這個特性的前兩種情況,第三種尚未實現。詳情檢視bug 932080。)

解構的實際應用

函式引數定義

作 為開發者,我們需要實現設計良好的API,通常的做法是為函式為函式設計一個物件作為引數,然後將不同的實際引數作為物件屬性,以避免讓API使用者記住 多個引數的使用順序。我們可以使用解構特性來避免這種問題,當我們想要引用它的其中一個屬性時,大可不必反覆使用這種單一引數物件。


    function removeBreakpoint({ url, line, column }) {
      // ...
    }

這是一段來自Firefox開發工具JavaScript偵錯程式(同樣使用JavaScript實現——沒錯,就是這樣!)的程式碼片段,它看起來非常簡潔,我們會發現這種程式碼模式特別討喜。

配置物件引數

延伸一下之前的示例,我們同樣可以給需要解構的物件屬性賦予預設值。當我們構造一個提供配置的物件,並且需要這個物件的屬性攜帶預設值時,解構特性就派上用場了。舉個例子,jQuery的ajax函式使用一個配置物件作為它的第二引數,我們可以這樣重寫函式定義:

    jQuery.ajax = function (url, {
      async = true,
      beforeSend = noop,
      cache = true,
      complete = noop,
      crossDomain = false,
      global = true,
      // ... 更多配置
    }) {
      // ... do stuff
    };

如此一來,我們可以避免對配置物件的每個屬性都重複var foo = config.foo || theDefaultFoo;這樣的操作。

(編者按:不幸的是,物件的預設值簡寫語法仍未在Firefox中實現,我知道,上一個編者按後的幾個段落講解的就是這個特性。點選bug 932080檢視最新詳情。)

與ES6迭代器協議協同使用

ECMAScript 6中定義了一個迭代器協議,我們在《深入淺出ES6(二):迭代器和for-of迴圈》中已經詳細解析過。當你迭代Maps(ES6標準庫中新加入的一種物件)後,你可以得到一系列形如[key, value]的鍵值對,我們可將這些鍵值對解構,更輕鬆地訪問鍵和值:

    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語言中尚未整合多重返回值的特性,但是無須多此一舉,因為你自己就可以返回一個數組並將結果解構:

    function returnMultipleValues() {
      return [1, 2];
    }
    var [foo, bar] = returnMultipleValues();

或者,你可以用一個物件作為容器併為返回值命名:

    function returnMultipleValues() {
      return {
        foo: 1,
        bar: 2
      };
    }
    var { foo, bar } = returnMultipleValues();

這兩個模式都比額外儲存一個臨時變數要好得多。

    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) => ...);

使用解構匯入部分CommonJS模組

你是否尚未使用ES6模組?還用著CommonJS的模組呢吧!沒問題,當我們匯入CommonJS模組X時,很可能在模組X中匯出了許多你根本沒打算用的函式。通過解構,你可以顯式定義模組的一部分來拆分使用,同時還不會汙染你的名稱空間:


    const { SourceMapConsumer, SourceNode } = require("source-map");

(如果你使用ES6模組,你一定知道在import宣告中有一個相似的語法。)

文後盤點

所以,正如你所見,解構在許多獨立小場景中非常實用。在Mozilla我們已經積累了許多有關解構的使用經驗。十年前,Lars Hansen在Opera中引入了JS解構特性,Brendan Eich隨後就給Firefox也增加了相應的支援,移植時版本為Firefox 2。所以我們可以肯定,漸漸地,你會在每天使用的語言中加入解構這個新特性,它可以讓你的程式碼變得更加精簡整潔。

相關推薦

深入淺出ES6 Destructuring

什麼是解構賦值? 解構賦值允許你使用類似陣列或物件字面量的語法將陣列和物件的屬性賦給各種變數。這種賦值語法極度簡潔,同時還比傳統的屬性訪問方法更為清晰。 通常來說,你很可能這樣訪問陣列中的前三個元素: var first = someArray[0]; va

深入淺出Mesos親身體會Apache Mesos

反饋 存儲 stat tar getting multi -a sources 其他 http://www.infoq.com/cn/articles/analyse-mesos-part-06 關於下一代數據中心操作系統Apache Mesos的系列文章,已經完成的內

es6module模塊export,import

導入 運行時 發現 let 腳本文件 推薦 必須 哪些 書寫 es6之前,社區模塊加載方案,主要是CommonJS(用於服務器)和AMD(用於瀏覽器) 而es6實現的模塊解決方案完全可以替代CommonJS和AMD ES6模塊設計思想:盡量靜態化,在編譯時就能確定模塊的依

深入淺出ES6十三類 Class

目前面臨的問題 假如我們想要建立一個經典的面向物件設計示例:Circle類。想象一下我們正在為一個簡單的Canvas庫編寫這個Circle類,在眾多需要考慮的因素中,我們可能更想了解以下功能的實現方式: 在給定的Canvas上繪製一個給定圓。跟蹤記錄生成圓的總數。跟蹤記錄給

Angular4學習筆記

程式碼已提交至GitHub 就目前為止,demo的結構已經被拆的很細很細。元件、服務、model之間已經分割開來,下一步的目標就是把介面做的好看一點。 目前介面看上去還是太簡單,在這個看臉的世界裡,完全活不下去。 至少做成這個樣子: 在這之前,還

深入淺出ES6學習Babel和Broccoli,馬上就用ES6

自ES6正式釋出,人們已經開始討論ES7:未來版本會保留哪些特性,新標準可能提供什麼樣的新特性。作為Web開發者,我們想知道如何發揮這一切的巨大能量。在深入淺出ES6系列之前的文章中,我們不斷鼓勵你開始在編碼中加入ES6新特性,輔以一些有趣的工具,你完全可以從現在開始使用E

Zookeeper詳Zookeeper的應用場景

很好 手動 app1 服務器 ros 運行時 dns 再次 -- Zookeeper是一個發布/訂閱模式的分布式數據管理與協調框架,結合Watcher事件通知,可以搭建分布式框架中的很多核心功能。數據發布和訂閱也就是常用的配置管理,將數據信息發布到一個或者多個ZK節點上,應

安卓專案實戰之強大的網路請求框架okGo使用詳擴充套件專案okServer,更強大的下載上傳功能,支援斷點和多工管理

OkGo與OkDownload的區別就是,OkGo只是簡單的做一個下載功能,不具備斷點下載,暫停等操作,但是這在很多時候已經能滿足需要了。 而有些app需要有一個下載列表的功能,就像迅雷下載一樣,每個下載任務可以暫停,可以繼續,可以重新下載,可以有下載優先順序,這時候OkDownload就有

【linux】Valgrind工具集詳使用Valgrind gdbserver和GDB除錯程式

一、概述 在Valgrind下執行的程式不是由CPU直接執行的。相反,它執行在Valgrind提供的合成CPU上。這就是偵錯程式在Valgrind上執行時無法除錯程式的原因。 二、快速入門 在使用Memcheck工具時使用GDB除錯程式,啟動方式如下: 1、valgrind

Tkinter 元件詳LabelFrame

Tkinter 元件詳解之LabelFrame LabelFrame 元件是 Frame 元件的變體。預設情況下,LabelFrame 會在其子元件的周圍繪製一個邊框以及一個標題。 何時使用 LabelFrame 元件? 當你想要將一些相關的元件分為一組的時候,可

各種音視訊編解碼學習詳之 編解碼學習筆記H.26x系列

    最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解成很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbi

SVM系列深入解析 序列最小最優化SMO演算法一

SMO演算法是幹啥的 首先要先搞明白一個基本的問題: SMO演算法是幹啥的? 通過前面的介紹,我們現在掌握了線性不可分支援向量機。其形式為如下的凸二次規劃: m

TensorFlow 從入門到精通tensorflow.nn 詳

看過前面的例子,會發現實現深度神經網路需要使用 tensorflow.nn 這個核心模組。我們通過原始碼來一探究竟。 # Copyright 2015 Google Inc. All Rights Reserved. # # Licensed under th

Pygame詳image 模組

pygame.image 用於影象傳輸的 Pygame 模組。 函式 pygame.image.load()  —  從檔案載入新圖片 pygame.image.save()  —  將影象儲

【深度分析Zigbee】Zstack協議棧初窺協調器的組網過程詳

這一講我要詳細說一下協調器的組網過程。在Zstack中,網路組網是從ZDApp_Init函式開始的。具體的執行流程為:Main()->osal_init_system()->osalInitTasks()->ZDApp_In it()。進入到ZDApp_I

Spring中的各種UtilsAopProxyUtils詳

原創文章,轉載請註明出處本節介紹AopProxyUtils,這個工具類方法本身很少,但是涉及到很多AOP相關重要介面,所以本節主要會對涉及到的介面進行初步的介紹;Class<?> ultimateTargetClass(Object candidate)獲取一個代

微服務詳部署與測試

獨立部署和使用諸如Docker的容器來部署微服務,使用Docker將專案部署到AWS上。 可以利用Docker或者任何其他容器,可以簡化部署; 1.使用Netflix OSS的微服務架構概述 Netflix是微服務架構中的先鋒,通過他

深入淺出學習Struts1框架ActionServlet的例項化

之前寫了五篇關於struts1框架學習的部落格,主要是從mvc的一個例項開始,慢慢重構出一個struts1框架雛形,通過這個雛形來引出我們要學習的struts1框架並且編寫了一個struts1例項。五篇部落格如下: 今天我們來分析一下先前的Struts1框

ES6語法2—— 物件賦值、箭頭函式使用和思考

    ES6的內容太過豐富,在學習的過程中,我選擇取其精華,跳過非精華,個人覺得變數的解構賦值和箭頭函式都是非常有實際使用價值的內容,就單獨拿出來放一個章節分析和介紹。     關於變數的解構賦值,其實有很多種解構方式,包括陣列,物件,字串等都可以進行解構賦值,但個人認為

物聯網平臺構架系列 Amazon, Microsoft, IBM IoT 解決方案導論 之 結語

物聯網; iot; aws; 亞馬遜; greengrass;microsoft; azure;ibm; watson; bluemix最近研究了一些物聯網平臺技術資料,以做選型參考。腦子裏積累大量信息,便想寫出來做一些普及。作為科普文章,力爭通俗易懂,不確保概念嚴謹性。我會給考據癖者提供相關英文鏈接,以便深