原文: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)



