摘要:被解構的數據項位于賦值運算符的右側,可以是任何數組和對象的組合,允許隨意嵌套。數組模式位于賦值運算符的左側,被結構的數組在其右側。
解構是ES6的新特性,用于從JavaScript對象和數組中提取數據,語法上比ES5所提供的更加簡潔、緊湊、清晰。它不僅能減少你的代碼量,還能從根本上改變你的編碼方式。用的越多,你就會發現越多塑造數據和函數的方式,這些實現方式在過去幾乎是不可能的。本文將深入探討解構賦值,為你介紹該新特性中你所需要知悉的一切。
什么是解構?解構與構造數據截然相反。 例如,它不是構造一個新的對象或數組,而是逐個拆分現有的對象或數組,來提取你所需要的數據。
ES6使用了一種新模式來匹配你想要提取的數值, 解構賦值就是采用了這種模式。 該模式會映射出你正在解構的數據結構,只有那些與該模式相匹配的數據,才會被提取出來。
被解構的數據項位于賦值運算符 = 的右側,可以是任何數組和對象的組合,允許隨意嵌套。用于給這些數據賦值的變量個數不限。
本文深入講解 解構賦值 中你所應知悉的知識點。如果想更好地理解它的工作原理,請參考 數組解構 和 對象解構。
數組解構數組解構 使用一個數組作為一個數據項,你可以根據 數組模式 (用于從數組中匹配你所需要的數值)從這個數組里面提取數值給一個或者多個變量賦值。
數組模式 是根據數值的位置來鑒別哪些值是你想要提取的。它必須能精確地映射數組的結構,來要讓數組模式中的每個變量都被賦上 被解構數組中 位置與之相對應的值。
舉幾個例子來幫助我們理解吧:
數組模式示例// 設置數組 const avengers = ["Tony Stark", "Steve Rogers", "Natasha Romanoff"]; // 把數組解構賦值給變量。數組模式位于賦值運算符 `=` 的左側,被結構的數組在 // 其右側。 const [ironMan, cap, blackWidow] = avengers; // ironMan = "Tony Stark" // cap = "Steve Rogers" // blackWidow = "Natasha Romanoff" // 輸出 ironMan: ironMan;
const avengers = ["Tony Stark", "Steve Rogers", "Natasha Romanoff"]; // 我們不用用到Tony const [, cap, blackWidow] = avengers; // ironMan = Error: undefined // cap = "Steve Rogers" // blackWidow = "Natasha Romanoff" // 輸出 cap: cap;
const avengers = ["Tony Stark", "Steve Rogers", "Natasha Romanoff"]; // cap 缺失 const [ironMan, , blackWidow] = avengers; // ironMan = "Tony Stark" // cap = Error: undefined // blackWidow = "Natasha Romanoff" // 輸出 blackWidow: blackWidow;
const avengers = ["Tony Stark", "Steve Rogers", "Natasha Romanoff"]; // ironMan vs cap const [ironMan, cap] = avengers; // ironMan = "Tony Stark" // cap = "Steve Rogers" // blackWidow = Error: undefined // 輸出 blackWidow: ironMan;嵌套數組
這種匹配模式也支持嵌套數組,只要保證賦值運算符 = 左側的數組模式與右側的數組結構相匹配即可。再次說明一下,= 左邊的變量都會被賦上 = 右側數組中位置與之相對應的值。 無論你怎么深層次地嵌套,仍可以對它們進行解構。
// Destructuring Nested Arrays const avengers = [ "Natasha Romanoff", ["Tony Stark", "James Rhodes"], ["Steve Rogers", "Sam Wilson"] ]; // Avengers and their partners const [blackWidow, [ironMan, warMachine], [cap, falcon]] = avengers; // blackWidow = "Natasha Romanoff" // ironMan = "Tony Stark" // warMachine = "James Rhodes" // cap = "Steve Rogers" // falcon = "Sam Wilson" // Output warMachine: warMachine;
// 從該數組中提取 Pepper Potts const avengers = [ "Natasha Romanoff", [["Tony Stark", "Pepper Potts"], "James Rhodes"], ["Steve Rogers", "Sam Wilson"] ]; // Destructure const [ , // 跳過 "Natasha Romanoff" [[ , // 跳過 "Tony Stark" hera // Pepper Potts 賦值給變量 "hera" ]]] = avengers; // 請注意:你也可以這樣寫 // const [, [[, hera ]]] = avengers; // 輸出 hera: hera; // hera = "Pepper Potts"運用rest操作符捕獲所有剩余項
如果你想要獲取特定的數組項,并且把剩余的項歸在一個數組,那么你可以這樣運用 rest操作符 來解構:
// 通過rest操作符解構 const avengers = ["Natasha Romanoff", "Tony Stark", "Steve Rogers"]; const [blackWidow, ...theOthers] = avengers; theOthers; // blackWidow = "Natasha Romanoff" // theOthers = ["Tony Stark", "Steve Rogers"] // 輸出 theOthers: theOthers;對象解構
對象解構就更神奇了,尤其是當你需要從一個復雜的、深層嵌套的對象中取值時,其作用更加明顯。重申一下,對象解構與數組解構用的是同樣的規則(即在賦值運算符左側創建一個 對象模式, 使它的變量位置與 = 右側對象的值位置相匹配)。
在對象解構中,你需要指明那些需要被提取值的屬性名稱,以及將要被賦值的變量名。跟數組解構一樣,我們需要在賦值運算符左邊先創建一個對象模式來映射被解構的對象。
盡管在這種情況下,我們想要提取的是 對象屬性的值 (如:我們從 { prop: value } 中提取 value)。相應地,我們的對象模式必須有一個變量,這個變量的位置要跟我們即將提取的屬性值所在的位置一致。
簡單示例我們可以這樣做,來將對象 { ironMan: "Tony Stark" } 的屬性 ironMan 的值 "Tony Stark" 賦值給變量 a:
//解構對象的屬性值,賦給單個變量 `a`: const { ironMan: a } = { ironMan: "Tony Stark" }; // 輸出 a: a; // a = "Tony Stark "
我們只要拓展相同的模式,就可以從一個對象中提取多個屬性值,如下:
// Setup our object const avengers = { ironMan: "Tony Stark", cap: "Steve Rogers", blackWidow: "Natasha Romanoff" }; // Destructure object to individual variables const { ironMan: a, cap: b, blackWidow: c } = avengers; // a = "Tony Stark " // b = "Steve Rogers" // c ="Natasha Romanoff" // Output a: a;
觀察一下這個解構模式是怎么確切地匹配 被解構對象 的。
嵌套的對象解構像解構嵌套數組一樣,我們可以對嵌套對象進行解構,不管它的層級多深。
// Setup our object const avengers = { blackWidow: "Natasha Romanoff", ironManCharacters: { couple: { ironMan: "Tony Stark", hera: "Pepper Potts", }, partner: { warMachine: "James Brodie" } }, capCharacters: { cap: "Steve Rogers", partner: { falcon: "Sam Wilson" } } }; // Destructure object to individual variables const { blackWidow: a, ironManCharacters: { couple: { ironMan: b, hera: c }, partner: { warMachine: d } }, capCharacters: { cap: e, partner: { falcon: f } } } = avengers; // a = "Natasha Romanoff" // b = "Tony Stark " // c = "Pepper Potts" // d = "James Brodie" // e = "Steve Rogers" // f = "Sam Wilson" // Output a: a;給賦值的變量命名
當然,把變量名設為諸如 a, b, c 之類,是很糟糕的,變量名稱應該是有意義的。
// Setup our object const avengers = { ironMan: "Tony Stark", cap: "Steve Rogers", blackWidow: "Natasha Romanoff" }; // Destructure object to individual variables with meaningful names const { ironMan: ironMan, cap: cap, blackWidow: blackWidow } = avengers; // blackWidow = "Natasha Romanoff" // ironMan = "Tony Stark " // cap = "Steve Rogers" // Output blackWidow: blackWidow;
這種做法比上面用 a,b,c 命名好,但是仍然可以完善。 { ironMan: ironMan } 看起來有點丑而且不直觀。
如果你要把一個對象的屬性值賦給一個變量,該變量的名稱跟對象的屬性名稱一樣,那么在 = 左側的賦值模式里面,你只需要簡單地寫屬性名即可,如下:
// Setup our object const avenger = { ironMan: "Tony Stark" }; // Destructure object to individual variables with meaningful names const { ironMan // equivalent to "ironMan: ironMan" } = avenger; // ironMan = "Tony Stark " // Output ironMan: ironMan;
由于 被解構的對象屬性名稱 跟 被賦值的變量名稱 相同,我們只需要把名稱列出來一次即可。
我們稍微重新修整下前面的代碼,就可以使它們看起來更加簡潔明了:
// Setup our object const avengers = { ironMan: "Tony Stark", cap: "Steve Rogers", blackWidow: "Natasha Romanoff" }; // Destructure object to individual variables with meaningful names const { ironMan, cap, blackWidow } = avengers; // Output ironMan: ironMan;從對象中提取一個深層嵌套的屬性
當我們要提取一個深層嵌套的對象屬性時,事情就更有趣了:
// Setup our object const avengers = { blackWidow: "Natasha Romanoff", ironManCharacters: { couple: { ironMan: "Tony Stark", hera: "Pepper Potts", }, partner: { warMachine: "James Brodie" } }, capCharacters: { cap: "Steve Rogers", partner: { falcon: "Sam Wilson" } } }; // Destructure a deeply nested object const { ironManCharacters: { couple } } = avengers; // couple = { // ironMan: "Tony Stark", // hera: "Pepper Potts", // } // Output couple: couple;
等等,你是怎么閱讀這段代碼的?couple 這個變量又是怎么被定義的呢?
通過這樣拆分,我們就可以看出賦值運算符 = 左側是被解構對象的一個映射:
const avengers = { ironManCharacters: { couple: { ironMan: "Tony Stark", hera: "Pepper Potts", } } }; const { ironManCharacters: { couple } } = avengers; // Output couple: couple;
僅僅使用 const { couple } = avengers; 并沒有辦法提取出 couple 的值。只有把要提取的對象屬性的 位置和名稱映射出來,JS 編譯器才能得到相應的信息,沿著對象的所有屬性往下查找,并準確地提取我們想要的值。
這里也要注意到 couple 用了語法捷徑給變量命名,實際上是這樣的:
const { ironManCharacters: { couple: couple } } = avengers;
couple 就是這樣被定義的,它的值就是對象 avengers 中屬性名為 couple 的值。
給對象的屬性解構賦值到目前為止,我們都是解構對象的值來給單個的變量賦值,其實還可以給另一個對象的屬性賦值。
const avengers = { blackWidow: "Natasha Romanoff", ironManCharacters: { couple: { ironMan: "Tony Stark", hera: "Pepper Potts" } } }; const ironManProperties = { family: {} }; ({ ironManCharacters: { couple: ironManProperties.family } } = avengers); ironManProperties.family // ironManProperties.family = { // ironMan: "Tony Stark", // hera: "Pepper Potts" // } // Output ironManProperties.family: ironManProperties.family;
在這里我們把 ironManCharacters.couple 的值賦給了 ironManProperties.family 這個屬性,這里有兩點需要說明一下:
1. 解構賦值必須被包含在 圓括號 內
當我們在對一個已存在的變量(如上面例子中的 ironManProperties)進行解構時,一定要這樣做,而不是去聲明一個新的變量。
2. 模式仍然相匹配
{ ironManCharacters: { couple... } } 與對象 avengers 中的 ironManCharacters 相匹配。這樣就能如你所愿,從 avengers 對象中提取出 ironManCharacters.couple 的值了。但是現在,couple 后面放置了一個新的對象ironManProperties 和它的屬性 family,其實被賦值的就是這個對象的屬性ironManProperties.family了。
當你嘗試把這種情況解釋清楚時,是否還有所困惑呢?在jsfiddle里面嘗試上面的代碼,一切就明了了。
如果你不清楚自己為什么要這樣做,請參考下一篇文章的例子。這些例子會告訴你,為什么采用這種模式來解構API調用的 JSON 對象,讓你領略解構的神奇之處!
默認值解構時,你還可以給變量指定一個默認值:
// Setup our object const avengers = { ironMan: "Tony Stark", cap: "Steve Rogers", blackWidow: "Natasha Romanoff" }; // Destructure using defaults const { ironMan, cap, blackWidow, theHulk="Bruce Banner" } = avengers; // ironMan = "Tony Stark" // cap = "Steve Rogers" // blackWidow = "Natasha Romanoff" // theHulk = "Bruce Banner" // Output blackWidow: blackWidow;解構時要避免出現這些問題 解構賦值時沒有使用 const, let, var
在講到對 對象屬性 進行解構賦值時就已經提及了這一點,但這里還是有必要再重申一下,讓大家有個深刻的印象。
不能對已經聲明的變量進行解構
也就是說,你只能在對變量解構賦值的同時聲明變量。
// Setup our object const avengers = { ironMan: "Tony Stark", cap: "Steve Rogers", blackWidow: "Natasha Romanoff", theHulk: "Bruce Banner" }; // Valid destructuring const { ironMan } = avengers; let { cap } = avengers; var { blackWidow } = avengers; // Invalid destructuring let theHulk; { theHulk } = avengers; // Error // Output theHulk: theHulk;
為何不能對一個已經聲明的變量進行解構呢?那是因為這時如果你使用了花括號 { ,JavaScript會認為你是在聲明一個 block。
解決的辦法就是把整個解構賦值用一對 圓括號 括起來。
// Setup our object const avengers = { ironMan: "Tony Stark", cap: "Steve Rogers", blackWidow: "Natasha Romanoff", theHulk: "Bruce Banner" }; // A valid Hulk let theHulk; ({ theHulk } = avengers); // theHulk = "Bruce Banner" // Output theHulk: theHulk;
現在我們不是以花括號開頭,所以JS不會認為我們是在聲明一個 block ,這樣就可以達到預期的解構結果。
直接返回一個被解構的值在沒有先聲明一個接下來要被返回的變量時,就直接返回一個被解構的值,這樣是無法達到預期效果的。例如,下面的代碼中,返回的將是整個 ironMan對象,而不是預期要的值 Tony Stark。
// Note: this doesn"t work! function getTonyStark(avengers){ return { ironMan: { realName } } = avengers; // return the avengers object, not the realName value } const avengers = { ironMan: { realName: "Tony Stark" } }; const tonyStark = getTonyStark(avengers); // tonyStark = { // ironMan: { // realName: "Tony Stark" // } // }; // Output tonyStark: tonyStark;
要從一個被解構的對象中提取值,必須先把它賦值給一個變量,然后再把這個變量返回,如下代碼所示:
// Note: this DOES work! function getTonyStark(avengers){ const { ironMan: { realName } } = avengers; return realName; } const avengers = { ironMan: { realName: "Tony Stark" } }; const tonyStark = getTonyStark(avengers); // tonyStark = "Tony Stark" // Output tonyStark: tonyStark;
這種把賦值和返回分成兩行代碼的做法實在惹人厭煩,代碼丑陋,也顯得沒必要。但很不幸,JavaScript就是這樣工作的----你必須先把解構的值賦給一個變量,然后再把它返回,兩步必須分開做。
但是,沒有說我們只是說分開做,并沒有說一定要擺成兩行代碼,所以像下面這樣寫成一行,也是能達到預期效果的:
function getTonyStark(avengers){ return ({ ironMan: { realName } } = avengers) && realName; } const avengers = { ironMan: { realName: "Tony Stark" } }; const tonyStark = getTonyStark(avengers); // tonyStark = "Tony Stark" // Output tonyStark: tonyStark;
由于JavaScript的 _short-circuit_ 邏輯操作符 (&& and ||) 會基于第一個操作數的值來返回第二個操作數的值,所以這種寫法能夠達到預期效果。這里,第一個操作數是解構賦值表達式,把值賦給 realName。而 realName 也就是第二個操作數,所以它的值最終被返回。
這樣做不是最佳的,但是能實現。在追求代碼簡短的同時,一定要注意代碼的可讀性。
總結本文深入講解了 解構賦值 的主要原則。雖然這樣讓你明白了解構是如果工作的,但是還不足以向你闡明如何真正運用這個強大的概念。
因此,下一篇文章,我會羅列一些高級的解構技巧,真正地展示解構的魔力,這些方式你可能從未思考過。
拓展閱讀The next post in this series
Array Destructuring Fiddle
Object Destructuring Fiddle
如果你還想閱讀更多,請看下面鏈接:
Speaking ES6 Chapter 10: Destructuring
Mozilla’s destructuring page
Mozilla Hacks on ES6 Destructuring
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/90824.html
摘要:前言在理想的狀態下,你可以在深入了解之前了解和開發的所有知識。繼承另一個類的類,通常稱為類或類,而正在擴展的類稱為類或類。這種類型的組件稱為無狀態功能組件。在你有足夠的信心構建用戶界面之后,最好學習。 原文地址:JavaScript Basics Before You Learn React 原文作者: Nathan Sebhastian 寫在前面 為了不浪費大家的寶貴時間,在開...
摘要:默認參數有了我們不需要再去檢測哪些值為并且給它們設定默認值了。我們甚至可以使用函數去找回默認參數的值注意這個函數只有在第二個參數省略時才會被調用。瀏覽器對默認參數的支持情況桌面瀏覽器移動端瀏覽器解構賦值解構賦值是的新特性。 原文地址:https://www.smashingmagazine.com/2016/07/how-to-use-arguments-and-parameters...
摘要:一共講解了個常用的新特性,講解過程也是由淺入深。最后一個新增的方法主要是為了彌補當做構造函數使用時產生的怪異結果。特性共享父級對象共享父級不能當做構造函數語法最簡表達式前后對比很容易理解,可以明顯看出箭頭函數極大地減少了代碼量。 showImg(https://segmentfault.com/img/bVQ5GW?w=1024&h=675); 上周在公司組織了 ES6 新特性的分享會...
閱讀 3705·2021-11-22 13:52
閱讀 3602·2019-12-27 12:20
閱讀 2385·2019-08-30 15:55
閱讀 2144·2019-08-30 15:44
閱讀 2262·2019-08-30 13:16
閱讀 574·2019-08-28 18:19
閱讀 1881·2019-08-26 11:58
閱讀 3436·2019-08-26 11:47