原文:http://www.nczonline.net/blog/2012/11/06/ecmascript-6-collections-part-3-weakmaps/
WeakMap
類似於常規的Map
的一點是,它們都是把一個值映射到某個唯一的鍵上,然后就可以使用這個鍵獲取到與之對應的值.WeakMap
和Map
不同的地方是,它的鍵只能是對象值而不可以是原始值.雖然這個限制看起來很奇怪,但正是這一點,才讓WeakMap
變得很有價值.
一個WeakMap
對象的鍵只持有其所引用對象的弱引用,弱引用的特點是,它不能阻止垃圾回收器回收其引用的對象.當那個對象被垃圾回收器銷毀后,WeakMap
對象中引用它的那個鍵值對也會被刪除.使用WeakMap
最典型的例子就是用在要創建一個與特定的DOM元素相關聯的對象的時候.例如,jQuery在程序內部維護着一些對象的緩存,每個緩存引用着一個DOM元素.使用WeakMap
的話,jQuery就可以在某些DOM元素被從文檔中刪除后就自動釋放它們先前占用的內存.
ECMAScript 6中的WeakMap
類型是一個無序的鍵值對列表,鍵必須是一個非null的對象(non-null object),值可以是任意類型.
WeakMap
的API很簡單,和Map
一樣,set()
和get()
分別用來添加數據和獲取數據:
var map = new WeakMap(),
element = document.querySelector(".element");
map.set(element, "Original");
// 下面就可以使用了
var value = map.get(element);
console.log(value); // "Original" // 下面刪除引用 element.parentNode.removeChild(element);
element = null;
//譯者注:下面這句實際上會報錯(value is not a non-null object),因為鍵必須是非null的對象.作者只是為了講解,看下面.
value = map.get(element);
console.log(value); // undefined
在這個例子中,我們存儲了一個鍵值對.鍵是一個DOM元素,存儲着一個對應的字符串值.隨后把那個DOM元素傳入get()
方法來獲取到存儲的字符串值.如果這個DOM元素被從文檔中刪除了,指向它的變量也被賦值為null
,然后WeakMap
對象中的那個鍵值對會被自動刪除,這時如果再讀取那個鍵值對的話,就會失敗.
這個例子有點誤導,因為在第二次調用map.get(element)
的時候,實際上是把null
(element
被賦的值)而不是DOM元素的引用傳進去了.你不能使用null
來作為WeakMap
對象的鍵(WeakMap的鍵只能是對象,null不是對象),這樣的代碼是會拋出異常的,根本不會執行到console.log語句.上面的代碼之所以那么寫,只是為了講解.想要說明那個鍵值對已經不存在了.不過不幸的是,我們沒有任何辦法來檢測這個鍵值對是否真的被刪除了(因為element
的值已經是null
,我們無法獲取到指向那個DOM元素的引用了,假如真能獲取到那個DOM對象的引用,傳給get方法的話,應該返回undefined).
譯者注:由於語文水平實在有限,上面的這段話我無法表達清楚.所以我用我拙劣的水平做了幾張圖,希望我的理解是對的,也希望你能看懂.
var map = new WeakMap(),變量map和element各自引用了一個對象.
element = document.querySelector(".element");
map.set(element, "Original");map中添加了一個鍵值對,這個鍵和變量element同時指向一個DOM對象,只是一個是強引用,一個是弱引用.
element.parentNode.removeChild(element);
element = null;強引用全部主動斷掉,最后剩下的弱引用也自動斷掉,孤立的DOM元素被回收,WeakMap對象中對應的鍵值對被自動清除.如果是常規的Map,由於不是弱引用,所以那個DOM對象不會被回收,仍然存在,仍然可以通過get()方法訪問到.
WeakMap
對象還有一個has()
方法,可以判斷某個對象引用是否作為了自己的鍵,還有delete()
方法,用來刪除一個鍵值對.
var map = new WeakMap(),
element = document.querySelector(".element");
map.set(element, "Original");
console.log(map.has(element)); // true console.log(map.get(element)); // "Original" map.delete(element);
console.log(map.has(element)); // false console.log(map.get(element)); // undefined
使用delete()
方法刪除某個鍵后,再執行has()
方法的時候會返回false
,執行get()
方法會返回undefined
.
瀏覽器支持
Firefox和Chrome都已經實現了WeakMap
,可是,在Chrome中,你必須手動開啟ECMAScript 6特性:打開chrome://flags
勾選"啟用實驗性 JavaScript".目前瀏覽器的實現都完全遵循了當前的strawman[1]規范(而ECMAScript 6的最新草案新添加了一個WeakMap.prototype.clear()
方法,這兩個瀏覽器目前還都沒實現).
用途和限制
目前WeakMap
有一個特定的使用情境,那就是在將值映射到一些未來可能被會刪除的對象上的時候.WeakMap
的這種"能釋放某些無用對象所占用的內存的能力"對於那種將DOM元素包裝成為某種自定義對象的JavaScript庫來說是很有用的(比如jQuery和YUI).在未來,WeakMap
的標准完全實現且被廣泛使用之后,應該會有更多的能用到WeakMap
的地方,所以在短期內,不要因為想不到使用WeakMap
的好點子而苦惱.
在大多數情況下,使用常規的Map
應該是你最合適的選擇.WeakMap
有不少限制,比如不能枚舉鍵值對(for of
),也不能得知它們到底包含了多少個鍵值對(size
).如果你需要這樣做,那么就使用常規的Map
.如果你僅僅需要把一些對象作為鍵,且不需要其他更多的功能,那么WeakMap
是個好的選擇.
參考
- WeakMapsStrawman (ECMA)