JavaScript WeakMap 與 Map 的差異及實際應用
此文章是 FrontendMaster 上的 Advanced Web Development Quiz 課程筆記
問題
What statements are true?
Select all the correct answers.
const userTokenMap = new WeakMap();
let user = { name: "Jane Doe", age: 24 };
userTokenMap.set(user, "secret_token");[1] userTokenMap implements the iterator protocol
[2] When setting user to null, userTokenMap.size returns 0
[3] If the user object is set to null, its userTokenMap entry can be garbage collected
[4] [...userTokenMap] returns an array of userTokenMap entriesWeakMap
WeakMap 與 Map 的使用方式相當類似,可以透過 key 來儲存任何形式的 value 到 WeakMap,但有幾個不一樣的地方:
WeakMap的 Key 必須是物件或非全域共享的symbolWeakMap不能被列舉,所以無法檢視 Weakmap 裡面全部存放的值WeakMap能使用的方法只有delete、get、has、set,不支援clear、size、keys、values,無法透過任何方式取得WeakMap中所儲存的所有 Key
而 WeakMap 與 Map 之間最大的差異在於垃圾回收的機制,Map 所儲存的 key 與 value 在沒有被特別移除的情況下會一直被參考,自然就可能造成記憶體洩漏,然而 WeakMap 的 key 參考的物件如果沒有其他地方在參考,就可以被自動回收,而不需要主動處理。
javascript
const wm = new WeakMap();
let obj = { id: 1 };
wm.set(obj, 'any-value');
wm.get(obj); // "any-value"
obj = null;
wm.get(obj); // undefined
// 此時 obj 原本參考的物件已無其他引用
// 垃圾回收器會在未來某個時間點回收該物件
// 之後嘗試存取會得到 undefined範例: DOM 物件當作 Key
因為 WeakMap 的 Key 是物件,我們可以把 DOM 元素作為 Key 來儲存對應元素的資訊,當該 DOM 元素從 DOM 上被移除時,就會自動進行垃圾回收
javascript
let myBtn = document.getElementById('btn');
let wm = new WeakMap();
wm.set(myBtn, { clickCnt: 0 });
myBtn.addEventListener(
'click',
function () {
let btnData = wm.get(myBtn);
btnData.clickCnt++;
},
false,
);範例: Class 物件當作 Key 實作真正的私有資料
WeakMap使用物件本身 (this) 作為 key,每個實體都有獨立的私有資料- 自動垃圾回收 - 當物件被銷毀時,WeakMap 中的條目也會自動清除
- 沒有任何反射方法能從物件本身發現或存取 WeakMap 中的資料。只有持有 WeakMap 引用的程式碼才能存取這些資料。
javascript
const name = new WeakMap();
const age = new WeakMap();
class Ding {
constructor() {
name.set(this, 'ding'); // 使用 this 作為 key
age.set(this, 50);
}
hello() {
return "I'm " + name.get(this) + ', ' + age.get(this) + ' years old';
}
}
class Dong {
constructor() {
name.set(this, 'dong'); // 使用 this 作為 key
age.set(this, 20);
}
hello() {
return "I'm " + name.get(this) + ', ' + age.get(this) + ' years old';
}
}答案
[3] If the user object is set to null, its userTokenMap entry can be garbage collected