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