ES6新式集合類(lèi)解析——Map、Set、WeakMap和WeakSet
譯文簡(jiǎn)介
多數(shù)主流編程語(yǔ)言都提供了若干種類(lèi)型的數(shù)據(jù)集合支持。例如,Python提供了列表、元組和詞典;Java語(yǔ)言中具有列表、集合、映射和隊(duì)列;Ruby提供了哈希表和數(shù)組。然而,JavaScript,直到現(xiàn)在,僅提供了對(duì)數(shù)組的支持。如你所知,對(duì)象和數(shù)組一直成為JavaScript編程的主力。目前,ES6新引入了四種新的數(shù)據(jù)結(jié)構(gòu),它們分別是:映射(Map)、集合(Set)、弱集合(WeakSet)和弱映射(WeakMap)。在本文中,讓我們一起學(xué)習(xí)這四種新增添的集合各自的優(yōu)勢(shì)吧。
ES5中HashMap的不足分析
散列、詞典和哈希表等是各種編程語(yǔ)言用來(lái)存儲(chǔ)鍵/值對(duì)這一類(lèi)數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。而且,通常情況下這些數(shù)據(jù)結(jié)構(gòu)都進(jìn)行了快速檢索方面的優(yōu)化處理。
在ES5,盡管可以使用JavaScript對(duì)象(它們其實(shí)就是一些帶有鍵和值的屬性的任意集合)來(lái)模擬哈希表,但還是存儲(chǔ)如下幾個(gè)不足的地方。
缺點(diǎn)之1:在ES5中鍵必須是字符串
JavaScript對(duì)象中的屬性鍵部分必須是字符串類(lèi)型,這就限制了它們作為不同數(shù)據(jù)類(lèi)型的鍵/值對(duì)的集合的能力。當(dāng)然,你可以強(qiáng)制把其他數(shù)據(jù)類(lèi)型轉(zhuǎn)換為字符串,但是這無(wú)疑會(huì)增加額外的系統(tǒng)負(fù)擔(dān)。
缺點(diǎn)之2:對(duì)象天生就不可迭代
對(duì)象并不是設(shè)計(jì)作為集合使用的;因此,沒(méi)有有效的方法來(lái)確定一個(gè)對(duì)象到底有多少屬性(例如,Object.keys的效率就很低)。當(dāng)您遍歷某對(duì)象的屬性時(shí),同時(shí)你也獲得了該對(duì)象的原型屬性。誠(chéng)然,你可以將可迭代的屬性添加到所有對(duì)象,但并不是所有的對(duì)象都為了用作集合而設(shè)計(jì)的。例如,您可以使用 for …in循環(huán)和hasOwnProperty()方法,但這只不是一種變通的解決方法而已。當(dāng)您遍歷對(duì)象的屬性時(shí),這些屬性不必以與插入它們時(shí)相同的順序進(jìn)行檢索。
缺點(diǎn)之3:與內(nèi)置方法命名可能發(fā)生沖突
對(duì)象都具有內(nèi)置的方法,如constructor、toString和valueOf。如果其中之一作為一個(gè)屬性添加到對(duì)象上,這很可能會(huì)導(dǎo)致沖突。當(dāng)然,您可以使用Object.create(null)來(lái)創(chuàng)建一個(gè)空的對(duì)象(它不繼承自object.prototype);但是,這仍然也只是一種變通的方法而已。
***的ES6提供了新的集合數(shù)據(jù)類(lèi)型;因此,再也不需要使用對(duì)象來(lái)進(jìn)行集合模擬并不得不忍受其帶來(lái)的不足了。
使用ES6中的MapCollection
映射是我們要學(xué)習(xí)的***個(gè)數(shù)據(jù)結(jié)構(gòu)(或者說(shuō)“集合”)。映射是任何類(lèi)型的值/鍵對(duì)的集合。你可以很容易地創(chuàng)建新的映射、添加/刪除值、遍歷鍵/值以及有效地確定其大小。下面是這種數(shù)據(jù)結(jié)構(gòu)提供的幾個(gè)關(guān)鍵的方法:
創(chuàng)建映射并使用其常用的方法
請(qǐng)參考下圖中的代碼來(lái)學(xué)習(xí)如何創(chuàng)建一個(gè)映射和映射中提供的常用方法的用法:
使用ES6中的SetCollection
集合是值的有序列表,其中的值是不允許重復(fù)的。集合不是像數(shù)組一樣進(jìn)行索引,而是通過(guò)鍵來(lái)訪(fǎng)問(wèn)的。事實(shí)上,集合早已經(jīng)存在于像Java、Ruby、Python及許多其他語(yǔ)言之中。ES6中的集合和其他語(yǔ)言中的集合的一個(gè)重要區(qū)別是,ES6中的集合是有順序的(在許多其他的語(yǔ)言卻不是這樣)。下圖給出集合的幾個(gè)關(guān)鍵方法的用法舉例:
弱集合、內(nèi)存與垃圾回收
JavaScript垃圾回收是一種內(nèi)存管理技術(shù)。在這種技術(shù)中,不再被引用的對(duì)象會(huì)被自動(dòng)刪除,而與其相關(guān)的資源也會(huì)被一同回收。
Map和Set中對(duì)象的引用都是強(qiáng)類(lèi)型化的,并不會(huì)允許垃圾回收。這樣一來(lái),如果Map和Set中引用了不再需要的大型對(duì)象,如已經(jīng)從DOM樹(shù)中刪除的DOM元素,那么其回收代價(jià)是昂貴的。
為了解決這個(gè)問(wèn)題,ES6還引入了另外兩種新的數(shù)據(jù)結(jié)構(gòu),即稱(chēng)為WeakMap和WeakSet的弱集合。這些集合之所以是“弱的”,是因?yàn)樗鼈冊(cè)试S從內(nèi)存中清除不再需要的被這些集合所引用的對(duì)象。
使用ES6中的WeakMap
WeakMap是我們要介紹的第三個(gè)新的ES6集合。WeakMap類(lèi)似于通常的映射(Map),盡管它提供了更少的方法支持以及存在與前面提到的與垃圾回收有關(guān)的不同處理方案。
請(qǐng)參考下圖中的代碼來(lái)了解這種數(shù)據(jù)結(jié)構(gòu)的創(chuàng)建及其少數(shù)的幾個(gè)方法的使用:
用例分析
網(wǎng)址http://stackoverflow.com/questions/29413222/what-are-the-actual-uses-of-es6-weakmap處提供了幾個(gè)很受歡迎的有關(guān)WeakMap的實(shí)例。它們可以用來(lái)使一個(gè)對(duì)象的私有數(shù)據(jù)“私有”,也可以用來(lái)跟蹤DOM節(jié)點(diǎn)/對(duì)象。
私有數(shù)據(jù)使用舉例
下面的示例是由JavaScript專(zhuān)家Nicholas C. Zakas先生提供的。
在這里,使用WeakMap簡(jiǎn)化了保持對(duì)象的私有數(shù)據(jù)真正“私有”的過(guò)程。你可以引用Person對(duì)象,但在沒(méi)有特定的Person實(shí)例的情況下對(duì)privateDataWeakMap的訪(fǎng)問(wèn)是不允許的。
DOM節(jié)點(diǎn)使用舉例
谷歌聚合項(xiàng)目(https://github.com/Polymer)中就在一段稱(chēng)為PositionWalker的代碼中使用了WeakMaps。其中的PositionWalker方法用于跟蹤一棵DOM子樹(shù)中的位置,這個(gè)“位置”對(duì)應(yīng)于當(dāng)前節(jié)點(diǎn)和距離該節(jié)點(diǎn)的偏移量。該方法中使用WeakMap來(lái)跟蹤DOM節(jié)點(diǎn)的編輯、刪除和變化等。
使用ES6中的WeakSet
WeakSet是集合(Set)的集合,當(dāng)不再需要該集合中的元素引用時(shí)可以把它們進(jìn)行垃圾收集。但是,WeakSet不允許迭代。有關(guān)它們的用法相當(dāng)有限(至少到目前還是很少見(jiàn))。大多數(shù)的早期采用者都說(shuō),WeakSet可用于標(biāo)記對(duì)象而不是改變它們。ES6-Features.org網(wǎng)站(http://es6-features.org/)上提供了一個(gè)從WeakSet中添加和刪除元素的例子,目的是為了記錄是否對(duì)象已被標(biāo)記過(guò),請(qǐng)參考下圖中代碼:
能否映射一切?
映射和集合都只是比較新穎的鍵/值對(duì)的集合。也就是說(shuō),在許多情況下JavaScript對(duì)象仍然可以用作集合。無(wú)需切換到的這些新的集合,除非形勢(shì)需要這樣做。
MDN網(wǎng)站(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)提供了一個(gè)問(wèn)題列表,你可以參考確定何時(shí)使用對(duì)象或何時(shí)使用鍵/值對(duì)的集合:
1.鍵是否在運(yùn)行時(shí)前一直是未知的?您需要?jiǎng)討B(tài)查找這些鍵嗎?
2.是否所有的值都具有相同的類(lèi)型并可以互換使用?
3.你真正需要不是字符串的鍵嗎?
4.你經(jīng)常添加或刪除鍵-值對(duì)嗎?
5.你是否有任意(很容易改變)數(shù)目的鍵-值對(duì)?
6.你的集合能夠迭代嗎?
小結(jié)
以前JavaScript集合被相當(dāng)有限地使用,但ES6改變了這一點(diǎn)。這些新的集合將為JavaScript語(yǔ)言添加強(qiáng)大功能和靈活性,從而簡(jiǎn)化JavaScript程序員的開(kāi)發(fā)任務(wù)。

































