目錄
弱引用(weak reference) & 垃圾回收
弱引用相對於強引用
,它們都和垃圾回收機制有關
目前除了C、C++、Rust以外多數語言
都有垃圾回收機制
當new出一個實例後,系統會進行記憶體分配,並回傳一個位置(address),這個位置就是指標(pointer)
沒有垃圾回收
的語言需要自己
進行記憶體管理
,否則會記憶體溢出
有垃圾回收
的語言,當物件不會再被用到後會由垃圾收集行程
(Garbage Collection)幫忙釋放記憶體
,開發者不用自行管理記憶體。效能會比沒有的語言差
比喻的話,前者是沒有清潔工,要自己去丟垃圾大樓,後者是有清潔工,清潔工會定期巡視,並幫你把垃圾拿去丟掉的大樓
後者效能較差原因主要有2個
- 清潔工要先巡視有沒有像是垃圾的東西(跑
垃圾收集行程
也會花費CPU資源
) - 清潔工還要判斷東西是不是垃圾(使用
Reference counting
,需要額外空間
進行標記)。至於前者是自己拿去丟,總不會不知道哪些是垃圾吧
WeakMap
WeakMap是由key和value組成的map,其key必須是物件
。WeakMap的key
是可以垃圾回收
的,且若key被回收
,其value也會
跟著被回收
WeakMap不可迭代
,故沒有.entries()、.values()、keys()、.forEach()之類的方法
也不能監測其key是否存活,故key
是不能列舉
的(not enumerable)。如果WeakMap物件要有一個方法可以列舉key的話,那列出的key因為受垃圾回收影響,每次得到的結果可能不同
WeakMap allows associating data to objects in a way that doesn't prevent the key objects from being collected, even if the values reference the keys. However, a WeakMap doesn't allow observing the liveness of its keys, which is why it doesn't allow enumeration; if a WeakMap exposed any method to obtain a list of its keys, the list would depend on the state of garbage collection, introducing non-determinism.
如果希望能列出key的話應該用Map
WeakMap vs Map
前一段提到Map
的key可列舉,故用key找Map中對應的value時有2個缺點
取值和存值都要迭代
key,時間複雜度為O(n)Map會有1個裝了key、1個裝了value的陣列,因為這2個陣列,所以Map的key才不會被回收,但因為這2個陣列,所以會有
記憶體流失
(memory leak)的問題In computer science, a memory leak is a type of resource leak that occurs when a computer program incorrectly manages memory allocations in a way that memory which is no longer needed is not released.
相反地WeakMap因為
key會被垃圾回收
,所以則沒有以上問題
儲存DOM Element
WeakMap其中一個常見的用法是監測DOM元素
因為WeakMap會被垃圾回收,所以DOM元素被移除後
WeakMap內對應的key
就會被回收
,故不
會多占記憶體
<body>
<button class="btn1">btn1</button>
<button class="btn2">btn2</button>
<script>
const btn = document.querySelector('.bnt1');
const btn2 = document.querySelector('.bnt2');
const wm = new WeakMap();
wm.set(btn, {clickCnt: 0});
wm.set(btn2, {clickCnt: 0});
btn.addEventListener('click', function() {
let btnData = wm.get(btn);
btnData.clickCnt++;
console.log(btnData);
}, false);
btn2.addEventListener('click', function() {
let btnData = wm.get(btn2);
btnData.clickCnt++;
console.log(btnData);
}, false);
console.log(wm);
// 點3下後的結果
// WeakMap {button.bnt1 => {…}}
// [[Entries]]
// 0: {button.bnt1 => Object}
// key: button.bnt1
// value: {clickCnt: 3}
// 移除DOM node後WeakMap key將會被回收
btn.remove();
console.log(wm); // ReferenceError: vm is not defined
</script>
</body>
另外Vue3的響應式系統
就是用WeakMap當作緩存
,以避免重復代理物件
參考資料
WeakMaps: Illustrated
ES6 WeakMaps, Sets, and WeakSets in Depth