昨天瞭解了兩種資料結構: Set & Map 後,其實還有另外兩個和它們相似的資料結構,也就是 WeakSet & WeakMap,來一起瞭解它們吧!
不過在那之前,要先來介紹一個和 WeakSet & WeakMap 很有關係的名詞 - 弱引用。
用簡化後的話來說,當一個物件沒有任何 reference 時,會被定期自動 GC 掉,例如以下圖片中,右側的三個物件因為都和 roots 物件,沒有直接或是間接的 reference,所以會被 GC。
這個是我們在 Day3 認識到的內容,若關於 GC 機制等觀念印象模糊的話可以回去看 Day3!
而這篇文章介紹的 WeakSet & WeakMap 它們比較特別,都是屬於弱引用,也就是說,若 WeakSet & WeakMap 引用了某個物件型別的值當作 key 值使用,若被引用的那個物件被設定成 null 並且沒有其他引用存在時,就會在未來某一時刻被自動 GC。
後面 WeakMap 介紹的第二點有範例說明,閱讀後會更加明確些。
而特別的是 Set & Map 對於物件的引用是強引用,強引用的話即使被當作 key 值的物件被修改成了 null,但因為 Map 還可以用它的一些方法例如 Map.prototype.keys()
做遍歷,所以還是有些方式可以取到該物件,當然不會自動 GC。
let ray = { name: "John" };
const map = new Map();
map.set(ray, "...");
ray = null; // 不會被回收,可透過 Map.keys() 取用
除了 WeakSet & WeakMap 使用了弱引用,還有其他地方有用到弱引用嗎?答案是有,JavaScript 有個內建的物件 WeakRef,就可以用來建立弱引用的物件,有興趣的讀者可以參考 MDN 文件:
WeakMap 和 Map 非常相似,兩者差異在於以下幾點:
Map 資料結構的 key可以是各種資料型態的值(字串/數字/物件…等),但 WeakMap 的 key 必須是物件型別。
這裡舉個範例,建立 WeakMap 和 Map 物件各一個,然後我們將它們的 key 值設定為 null,雖然當下兩個物件都還能保持它們的 key value,但過一段不定期的時間後觸發 GC 後,WeakMap 的 key 就會被回收,像截圖是隔十分鐘後再重新印出會發現 WeakMap 已被清空。
範例程式碼:
let keyObj = {};
const exampleWM = new WeakMap();
exampleWM.set(keyObj, 'example');
keyObj = null;
console.log(exampleWM);
let keyObj2 = {};
const exampleMap = new Map();
exampleMap.set(keyObj2, 'example');
keyObj2 = null;
console.log(exampleMap);
WeakMap 不像 Map 有 keys()、forEach()、entries()...等方法,實際上 WeakMap 也只有四個方法可以使用而已,可參考: MDN WeakMap Instance methods
以個人經驗來說,我自己是還沒在實務開發用到 WeakMap,它確實不常被使用到,但它可以有效的做垃圾回收,所以只需要短暫在瀏覽器存放額外資料的情境就適合使用它,例如過一段時間就清除的快取之類的。
這邊分享一位日本高手工程師 Daishi Kato 和他的 Github,他貢獻了多個和 React 相關的開源專案,其中一個專案 proxy-memoize 就有用到 WeakMap 去做快取機制,從 proxy-memoize 的原始碼 可以看到多處都有使用到 WeakMap,以下節錄有使用到的地方:
type Affected = WeakMap<object, Set<string | number | symbol>>; // line 8,搭配 WeakMap 定義一個 Type alias(型別別名)
const resultCache = new WeakMap<Obj, Entry>(); // line 70,存放 cache 的地方
const proxyCache = new WeakMap(); // line 70
// line 82 也有用到
const affected: Affected = new WeakMap(); // line 88,使用 line 8 定義的 Type alias 並給予初始值 WeakMap
如同 WeakMap 和 Map 的關係,WeakSet 和 Set 也非常相似,兩者差異在於以下幾點:
若其他地方沒有引用到 WeakSet 內部的值,會不定期被 GC。
WeakSet 它只有 add()、has()、delete() 等方法,並沒有 Set 的 size 和 keys() 方法。
我自己也還沒在實務開發用到 WeakSet,搜尋了網路上在實際開發上的應用也沒有找到,但還是一樣的原則,如果只是需要短暫暫存資料,未來不需要就移除的話,就可以使用它。
這篇就到這邊啦~若讀者們知道更多關於 WeakMap & WeakSet 的實際應用範例的話,非常歡迎留言補充,我也想知道~