可達(dá)性
通常,當(dāng)對象、數(shù)組之類的數(shù)據(jù)結(jié)構(gòu)在內(nèi)存中時,它們的子元素,如對象的屬性、數(shù)組的元素都被認(rèn)為是可達(dá)的。
如果使用對象作為常規(guī) Map 的鍵,那么當(dāng) Map 存在時,該對象也將存在。它會占用內(nèi)存,并且不會被(垃圾回收機(jī)制)回收。
例如:
let john = {name: "John”};
let map = new Map();
map.set(john, '...');
//john 被存儲在了 map 中,
//我們可以使用 map.keys() 來獲取它
WeakMap 在這方面有著根本的不同。它不會阻止垃圾回收機(jī)制作為對鍵的對象(key object)的回收。
WeakMap
WeakMap 和 Map 的第一個不同點就是,WeakMap 的鍵必須是對象,不能是原始值:
let weakMap = new WeakMap();
let obj = {};
weakMap.set(obj, "ok"); //正常工作(以對象為鍵)
//不能使用字符串作為鍵
weakMap.set("test", "Whoops"); //Error,因為 “test” 不是一個對象
現(xiàn)在,如果我們在 weakMap 中使用一個對象作為鍵,并且沒有其他對這個對象的引用 —— 該對象將會被從內(nèi)存(和map)中自動清除。
let john = {name: "John"};
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; //覆蓋引用
//john 被從內(nèi)存中刪除了!
如果 john 僅僅是作為 WeakMap 的鍵而存在 —— 它將會從 map (和內(nèi)存中)自動刪除。
WeakMap 不支持迭代以及 keys(),values() 和 entries() 方法。所以沒有辦法獲取 WeakMap 的所有鍵或值。
WeakMap 只有以下的方法:
- `weakMap.get(key)
- `weakMap.set(key, value)
- `weakMap.delete(key)
- `weakMap.has(key)
為什么會有這種限制呢?這是技術(shù)的原因。如果一個對象丟失了其它所有引用(就像上面示例中的 john),那么它就會被垃圾回收機(jī)制自動回收。但是在從技術(shù)的角度并不能準(zhǔn)確知道 何時會被回收。
這些都是由 JavaScript 引擎決定的。JavaScript 引擎可能會選擇立即執(zhí)行內(nèi)存清理,如果現(xiàn)在正在發(fā)生很多刪除操作,那么 JavaScript 引擎可能就會選擇等一等,稍后再進(jìn)行內(nèi)存清理。因此,從技術(shù)上講,WeakMap 的當(dāng)前元素的數(shù)量是未知的。JavaScript 引擎可能清理了其中的垃圾,可能沒清理,也可能清理了一部分。因此,暫不支持訪問 WeakMap 的所有鍵/值的方法。
那么,在哪里我們會需要這樣的數(shù)據(jù)結(jié)構(gòu)呢?
WeakSet
WeakSet 的表現(xiàn)類似:
- 與
Set類似,但是我們只能向WeakSet添加對象(而不是原始值)。 - 對象只有在其它某個(些)地方被訪問的時候,才能留在 set 中。
- 跟
Set一樣,WeakSet支持add,has和delete方法,但不支持size和keys()`,并且不可迭代。
變“弱(weak)”的同時,它也可以作為額外的存儲空間。但并非針對任意數(shù)據(jù),而是針對“是/否”的事實。WeakSet 的元素可能代表著有關(guān)該對象的某些信息。
例如,我們可以將用戶添加到 WeakMap 中,以追蹤訪問過我們網(wǎng)站的用戶:
let visitedSet = new WeakSet();
let john = {name: "John"};
let pete = {name: "Pete"};
let mary = {name: "Mary"};
visitedSet.add(john); //John 訪問了我們
visitedSet.add(pete); //然后是 Pete
visitedSet.add(john); //John 再次訪問
//visitedSet 現(xiàn)在有兩個用戶了
//檢查 John 是否來訪過?
alert(visitedSet.has(john)); //true
//檢查 Mary 是否來訪過?
alert(visitedSet.has(mary)); //false
john = null;
//visitedSet 將被自動清理(即自動清理其中已失效的值 john)
WeakMap 和 WeakSet 最明顯的局限性就是不能迭代,并且無法獲取所有當(dāng)前內(nèi)容。那樣可能會造成不便,但是并不會阻止 WeakMap/WeakSet 完成其主要工作 —— 成為在其它地方管理/存儲“額外”的對象數(shù)據(jù)。