咱們常常定義許多對象和數組,而後有組織地從中提取相關的信息片斷。在ES6中添加了能夠簡化這種任務的新特性:解構。解構是一種打破數據結構,將其拆分爲更小部分的過程。本文將詳細介紹ES6解構賦值node
在ES5中,開發者們爲了從對象和數組中獲取特定數據並賦值給變量,編寫了許多看起來同質化的代碼算法
let options = { repeat: true, save: false }; // 從對象中提取數據 let repeat = options.repeat, save = options.save;
這段代碼從options對象中提取repeat和save的值,並將其存儲爲同名局部變量,提取的過程極爲類似數組
若是要提取更多變量,則必須依次編寫相似的代碼來爲變量賦值,若是其中還包含嵌套結構,只靠遍歷是找不到真實信息的,必需要深刻挖掘整個數據結構才能找到所需數據cookie
因此ES6添加了解構功能,將數據結構打散的過程變得更加簡單,能夠從打散後更小的部分中獲取所需信息數據結構
對象字面量的語法形式是在一個賦值操做符左邊放置一個對象字面量dom
let node = { type: "Identifier", name: "foo" }; let { type, name } = node; console.log(type); // "Identifier" console.log(name); // "foo"
在這段代碼中,node.type的值被存儲在名爲type的變量中;node.name的值被存儲在名爲name的變量中函數
【解構賦值】prototype
到目前爲止,咱們已經將對象解構應用到了變量的聲明中。然而,咱們一樣能夠在給變量賦值時使用解構語法設計
let node = { type: "Identifier", name: "foo" }, type = "Literal", name = 5; // 使用解構來分配不一樣的值 ({ type, name } = node); console.log(type); // "Identifier" console.log(name); // "foo"
在這個示例中,聲明變量type和name時初始化了一個值,在後面幾行中,經過解構賦值的方法,從node對象讀取相應的值從新爲這兩個變量賦值rest
[注意]必定要用一對小括號包裹解構賦值語句,JS引擎將一對開放的花括號視爲一個代碼塊。語法規定,代碼塊語句不容許出如今賦值語句左側,添加小括號後能夠將塊語句轉化爲一個表達式,從而實現整個解構賦值過程
解構賦值表達式的值與表達式右側(也就是=右側)的值相等,如此一來,在任何可使用值的地方均可以使用解構賦值表達式
let node = { type: "Identifier", name: "foo" }, type = "Literal", name = 5; function outputInfo(value) { console.log(value === node); // true } outputInfo({ type, name } = node); console.log(type); // "Identifier" console.log(name); // "foo"
調用outputlnfo()函數時傳入了一個解構表達式,因爲JS表達式的值爲右側的值,於是此處傳入的參數等同於node,且變量type和name被從新賦值,最終將node傳入outputlnfo()函數
[注意]解構賦值表達式(也就是=右側的表達式)若是爲null或undefined會致使程序拋出錯誤。也就是說,任未嘗試讀取null或undefined的屬性的行爲都會觸發運行時錯誤
【默認值】
使用解構賦值表達式時,若是指定的局部變量名稱在對象中不存在,那麼這個局部變量會被賦值爲undefined
let node = { type: "Identifier", name: "foo" }; let { type, name, value } = node; console.log(type); // "Identifier" console.log(name); // "foo" console.log(value); // undefined
這段代碼額外定義了一個局部變量value,而後嘗試爲它賦值,然而在node對象上,沒有對應名稱的屬性值,因此像預期中的那樣將它賦值爲undefined
當指定的屬性不存在時,能夠隨意定義一個默認值,在屬性名稱後添加一個等號(=)和相應的默認值便可
let node = { type: "Identifier", name: "foo" }; let { type, name, value = true } = node; console.log(type); // "Identifier" console.log(name); // "foo" console.log(value); // true
在此示例中,爲變量value設置了默認值true,只有當node上沒有該屬性或者該屬性值爲undefined時該值才生效。此處沒有node.value屬性,由於value使用了預設的默認值
【爲非同名局部變量賦值】
若是但願使用不一樣命名的局部變量來存儲對象屬性的值,ES6中的一個擴展語法能夠知足需求,這個語法與完整的對象字面量屬性初始化程序的很像
let node = { type: "Identifier", name: "foo" }; let { type: localType, name: localName } = node; console.log(localType); // "Identifier" console.log(localName); // "foo"
這段代碼使用瞭解構賦值來聲明變量localType和localName,這兩個變量分別包含node.type和node.name屬性的值。type:localType語法的含義是讀取名爲type的屬性並將其值存儲在變量localType中,這種語法實際上與傳統對象字面量的語法相悖,原來的語法名稱在冒號左邊,值在右邊;如今值在冒號右邊,而對象的屬性名在左邊
當使用其餘變量名進行賦值時也能夠添加默認值,只需在變量名後添加等號和默認值便可
let node = { type: "Identifier" }; let { type: localType, name: localName = "bar" } = node; console.log(localType); // "Identifier" console.log(localName); // "bar"
在這段代碼中,因爲node.name屬性不存在,變量被默認賦值爲"bar"
【嵌套對象解構】
解構嵌套對象仍然與對象字面量的語法類似,能夠將對象拆解以獲取想要的信息
let node = { type: "Identifier", name: "foo", loc: { start: { line: 1, column: 1 }, end: { line: 1, column: 4 } } }; let { loc: { start }} = node; console.log(start.line); // 1 console.log(start.column); // 1
在這個示例中,咱們在解構模式中使用了花括號,其含義爲在找到node對象中的loc屬性後,應當深刻一層繼續查找start屬性。在上面的解構示例中,全部冒號前的標識符都表明在對象中的檢索位置,其右側爲被賦值的變量名;若是冒號後是花括號,則意味着要賦予的最終值嵌套在對象內部更深的層級中
更進一步,也可使用一個與對象屬性名不一樣的局部變量名
let node = { type: "Identifier", name: "foo", loc: { start: { line: 1, column: 1 }, end: { line: 1, column: 4 } } }; // 提取 node.loc.start let { loc: { start: localStart }} = node; console.log(localStart.line); // 1 console.log(localStart.column); // 1
在這個版本中,node.loc.start被存儲在了新的局部變量localStart中。解構模式能夠應用於任意層級深度的對象,且每一層都具有同等的功能
與對象解構的語法相比,數組解構就簡單多了,它使用的是數組字面量,且解構操做所有在數組內完成,而不是像對象字面量語法同樣使用對象的命名屬性
let colors = [ "red", "green", "blue" ]; let [ firstColor, secondColor ] = colors; console.log(firstColor); // "red" console.log(secondColor); // "green"
在這段代碼中,咱們從colors數組中解構出了"red"和"green"這兩個值,並分別存儲在變量firstColor和變量secondColor中。在數組解構語法中,咱們經過值在數組中的位置進行選取,且能夠將其存儲在任意變量中,未顯式聲明的元素都會直接被忽略
在解構模式中,也能夠直接省略元素,只爲感興趣的元素提供變量名
let colors = [ "red", "green", "blue" ]; let [ , , thirdColor ] = colors; console.log(thirdColor); // "blue"
這段代碼使用解構賦值語法從colors中獲取第3個元素,thirdColor前的逗號是前方元素的佔位符,不管數組中的元素有多少個,均可以經過這種方法提取想要的元素,不須要爲每個元素都指定變量名
【解構賦值】
數組解構也可用於賦值上下文,但不須要用小括號包裹表達式,這一點與對象解構不一樣
let colors = [ "red", "green", "blue" ], firstColor = "black", secondColor = "purple"; [ firstColor, secondColor ] = colors; console.log(firstColor); // "red" console.log(secondColor); // "green"
這段代碼中的解構賦值與上一個數組解構示例相差無幾,惟一的區別是此處的firstColor變量和secondColor變量已經被定義了
【變量交換】
數組解構語法還有一個獨特的用例:交換兩個變量的值。在排序算法中,值交換是一個很是常見的操做,若是要在ES5中交換兩個變量的值,則須引入第三個臨時變量
// 在 ES5 中互換值 let a = 1, b = 2, tmp; tmp = a; a = b; b = tmp; console.log(a); // 2 console.log(b); // 1
在這種變量交換的方式中,中間變量tmp不可或缺。若是使用數組解構賦值語法,就再也不須要額外的變量了
在這個示例中,數組解構賦值看起來像是一個鏡像:賦值語句左側(也就是等號左側)與其餘數組解構示例同樣,是一個解構模式;右側是一個爲交換過程建立的臨時數組字面量。代碼執行過程當中,先解構臨時數組,將b和a的值複製到左側數組的前兩個位置,最終結果是變量互換了它們的值
[注意]若是右側數組解構賦值表達式的值爲null或undefined,則會致使程序拋出錯誤
【默認值】
也能夠在數組解構賦值表達式中爲數組中的任意位置添加默認值,當指定位置的屬性不存在或其值爲undefined時使用默認值
let colors = [ "red" ]; let [ firstColor, secondColor = "green" ] = colors; console.log(firstColor); // "red" console.log(secondColor); // "green"
在這段代碼中,colors數組中只有一個元素,secondColor沒有對應的匹配值,可是它有一個默認值"green",因此最終secondColor的輸出結果不會是undefined
【嵌套數組解構】
嵌套數組解構與嵌套對象解構的語法相似,在原有的數組模式中插入另外一個數組模式,便可將解構過程深刻到下一個層級
let colors = [ "red", [ "green", "lightgreen" ], "blue" ]; // 隨後 let [ firstColor, [ secondColor ] ] = colors; console.log(firstColor); // "red" console.log(secondColor); // "green"
在此示例中,變量secondColor引用的是colors數組中的值"green",該元素包含在數組內部的另外一個數組中,因此seconColor兩側的方括號是一個必要的解構模式。一樣,在數組中也能夠無限深刻去解構,就像在對象中同樣
【不定元素】
函數具備不定參數,而在數組解構語法中有一個類似的概念——不定元素。在數組中,能夠經過...語法將數組中的其他元素賦值給一個特定的變量
let colors = [ "red", "green", "blue" ]; let [ firstColor, ...restColors ] = colors; console.log(firstColor); // "red" console.log(restColors.length); // 2 console.log(restColors[0]); // "green" console.log(restColors[1]); // "blue"
數組colors中的第一個元素被賦值給了firstColor,其他的元素被賦值給restColors數組,因此restColors中包含兩個元素:"green"和"blue"。不定元素語法有助於從數組中提取特定元素並保證其他元素可用
【數組複製】
在ES5中,開發者們常用concat()方法來克隆數組
// 在 ES5 中克隆數組 var colors = [ "red", "green", "blue" ]; var clonedColors = colors.concat(); console.log(clonedColors); //"[red,green,blue]"
concat()方法的設計初衷是鏈接兩個數組,若是調用時不傳遞參數就會返回當前函數的副本
在ES6中,能夠經過不定元素的語法來實現相同的目標
// 在 ES6 中克隆數組 let colors = [ "red", "green", "blue" ]; let [ ...clonedColors ] = colors; console.log(clonedColors); //"[red,green,blue]"
在這個示例中,咱們經過不定元素的語法將colors數組中的值複製到clonedColors數組中
[注意]在被解構的數組中,不定元素必須爲最後一個條目,在後面繼續添加逗號會致使程序拋出語法錯誤
能夠混合使用對象解構和數組解構來建立更多複雜的表達式,如此一來,能夠從任何混雜着對象和數組的數據解構中提取想要的信息
let node = { type: "Identifier", name: "foo", loc: { start: { line: 1, column: 1 }, end: { line: 1, column: 4 } }, range: [0, 3] }; let { loc: { start }, range: [ startIndex ] } = node; console.log(start.line); // 1 console.log(start.column); // 1 console.log(startIndex); // 0
這段代碼分別將node.loc.start和node.range[0]提取到變量start和startlndex中
解構模式中的loc和range僅表明它們在node對象中所處的位置(也就是該對象的屬性)。當使用混合解構的語法時,則能夠從node提取任意想要的信息。這種方法極爲有效,尤爲是從JSON配置中提取信息時,再也不須要遍歷整個結構了
【解構參數】
解構能夠用在函數參數的傳遞過程當中,這種使用方式更特別。當定義一個接受大量可選參數的JS函數時,一般會建立一個可選對象,將額外的參數定義爲這個對象的屬性
// options 上的屬性表示附加參數 function setCookie(name, value, options) { options = options || {}; let secure = options.secure, path = options.path, domain = options.domain, expires = options.expires; // 設置 cookie 的代碼 } // 第三個參數映射到 options setCookie("type", "js", { secure: true, expires: 60000 });
許多JS庫中都有相似的setCookie()函數,而在示例函數中,name和value是必需參數,而secure、path、domain和expires則否則,這些參數相對而言沒有優先級順序,將它們列爲額外的命名參數也不合適,此時爲options對象設置同名的命名屬性是一個很好的選擇。如今的問題是,僅查看函數的聲明部分,沒法辨識函數的預期參數,必須經過閱讀函數體才能夠肯定全部參數的狀況
若是將options定義爲解構參數,則能夠更清晰地瞭解函數預期傳入的參數。解構參數須要使用對象或數組解構模式代替命名參數
function setCookie(name, value, { secure, path, domain, expires }) { // 設置 cookie 的代碼 } setCookie("type", "js", { secure: true, expires: 60000 });
這個函數與以前示例中的函數具備類似的特性,只是如今使用解構語法代替了第3個參數來提取必要的信息,其餘參數保持不變,可是對於調用setCookie()函數的使用者而言,解構參數變得更清晰了
【必須傳值的解構參數】
解構參數有一個奇怪的地方,默認狀況下,若是調用函數時不提供被解構的參數會致使程序拋出錯誤
// 出錯! setCookie("type", "js");
缺失的第3個參數,其值爲undefined,而解構參數只是將解構聲明應用在函數參數的一個簡寫方法,其會致使程序拋出錯誤。當調用setCookie()函數時,JS引擎實際上作了如下這些事情
function setCookie(name, value, options) { let { secure, path, domain, expires } = options; // 設置 cookie 的代碼 }
若是解構賦值表達式的右值爲null或undefined,則程序會報錯。同理,若調用setCookie()函數時不傳入第3個參數,也會致使程序拋出錯誤
若是解構參數是必需的,大可忽略掉這些問題;但若是但願將解構參數定義爲可選的,那麼就必須爲其提供默認值來解決這個問題
function setCookie(name, value, { secure, path, domain, expires } = {}) { // ... }
這個示例中爲解構參數添加了一個新對象做爲默認值,secure、path、domain及expires這些變量的值所有爲undefined,這樣即便在調用setCookie()時未傳遞第3個參數,程序也不會報錯
【默認值】
能夠爲解構參數指定默認值,就像在解構賦值語句中那樣,只需在參數後添加等號而且指定一個默認值便可
function setCookie(name, value, { secure = false, path = "/", domain = "example.com", expires = new Date(Date.now() + 360000000) } = {} ) { // ... }
在這段代碼中,解構參數的每個屬性都有默認值,從而無須再逐一檢查每個屬性是否都有默認值。然而,這種方法也有不少缺點。首先,函數聲明變得比之前複雜了;其次,若是解構參數是可選的,那麼仍然要給它添加一個空對象做爲參數,不然像setCookie("type","js")這樣的調用會致使程序拋出錯誤
對於對象類型的解構參數,最好爲其賦予相同解構的默認參數
function setCookie(name, value, { secure = false, path = "/", domain = "example.com", expires = new Date(Date.now() + 360000000) } = { secure : false, path : "/", domain : "example.com", expires : new Date(Date.now() + 360000000) } ) { // ... }
如今函數變得更加完整了,第一個對象字面量是解構參數,第二個爲默認值。可是這會形成很是多的代碼冗餘,能夠將默認值提取到一個獨立對象中,而且使用該對象做爲解構和默認參數的一部分,從而消除這些冗餘
const setCookieDefaults = { secure : false, path : "/", domain : "example.com", expires : new Date(Date.now() + 360000000) } function setCookie(name, value,{ secure = setCookieDefaults.secure, path = setCookieDefaults.path, domain = setCookieDefaults.domain, expires = setCookieDefaults.expires }=setCookieDefaults) { // ... }
在這段代碼中,默認值已經被放到setCookieDefaults對象中,除了做爲默認參數值外,在解構參數中能夠直接使用這個對象來爲每個綁定設置默認參數。使用解構參數後,不得不面對處理默認參數的複雜邏輯,但它也有好的一面,若是要改變默認值,能夠當即在setCookieDefaults中修改,改變的數據將自動同步到全部出現過的地方
【字符串解構】
字符串也能夠解構賦值。這是由於,字符串被轉換成了一個相似數組的對象
const [a, b, c, d, e] = 'hello'; console.log(a);//"h" console.log(b);//"e" console.log(c);//"l" console.log(d);//"l" console.log(e);//"o"
相似數組的對象都有一個length
屬性,所以還能夠對這個屬性解構賦值
const {length} = 'hello'; console.log(length);//5
【數值和布爾值解構】
解構賦值時,若是等號右邊是數值和布爾值,則會先轉爲對象
let {toString:s1} = 123; console.log(s1 === Number.prototype.toString);//true let {toString:s2} = true; console.log(s2 === Boolean.prototype.toString);//true
解構賦值的規則是,只要等號右邊的值不是對象或數組,就先將其轉爲對象。因爲undefined
和null
沒法轉爲對象,因此對它們進行解構賦值,都會報錯
let { prop: x } = undefined; // TypeError let { prop: y } = null; // TypeError