Skip to content

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 entries

WeakMap

WeakMapMap 的使用方式相當類似,可以透過 key 來儲存任何形式的 value 到 WeakMap,但有幾個不一樣的地方:

  • WeakMap 的 Key 必須是物件或非全域共享的 symbol
  • WeakMap 不能被列舉,所以無法檢視 Weakmap 裡面全部存放的值
  • WeakMap 能使用的方法只有 deletegethasset,不支援 clearsizekeysvalues,無法透過任何方式取得 WeakMap 中所儲存的所有 Key

WeakMapMap 之間最大的差異在於垃圾回收的機制,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

參考

最後更新時間:

0 %
MIT Licensed | Copyright © 2025-present Wen-Hsiu's Blog