Weakmap詳解
- 2022 年 6 月 17 日
- 筆記
- javascript
先看一個例子
let obj = { name: 'toto' }
// { name: 'toto' }這個對象能夠被讀取到,因為obj這個變數名有對它的引用
// 將引用覆蓋掉
obj = null
// 這個對象將會被從記憶體中移除,因為我們已經失去了對它所有的引用
再來看另外一個例子
let obj = { name: 'toto' }
let arr = [ obj ]
obj = null
在這個例子中,對象{name:'toto'}不會被從記憶體中移除,因為數組arr保存了對它的引用
強引用和弱引用之間有什麼區別呢?
事實上,javascript中的大多數變數都保存著對一個對象的強引用。比如上面這個數組保存著對對象{name:'toto'}的強引用
如果一個變數保存著對一個對象的強引用,那麼這個對象將不會被垃圾回收,但是如果一個變數只保存著對這個對象的弱引用,那麼這個對象將會被垃圾回收
一些變數類型在對象上有一個弱引用,這就是Weakmap的情況
Weakmap
weakmap是一個額外的數據存儲,它可以讓我們從外部(第三方庫)擴展或者封裝一個對象,而不需要進行垃圾回收的推斷,或者能夠智慧的創建一個快取函數。
不用擔心看不明白,在比較map和weakmap之前我將解釋並展示它的含義。
Map和Weakmap的比較
使用map,對象會佔用記憶體,可能不會被垃圾回收。Map對一個對象是強引用
let obj = { name: 'toto' }
let mapObj = new Map()
mapObj.set(obj, 'any value')
obj = null
mapObj.size() // 1
Weakmap則是完全不同的,它不會阻止關鍵對象的垃圾回收
第一條規則,weakmap只接受object作為key,第二條規則是它只保存對對象的弱引用。
let obj = { name: 'toto' }
let weakmapObj = new WeakMap()
weakmapObj.set(obj, 'any value')
obj = null
weakmapObj .size() // 0
對象被垃圾回收器刪除,因為weakmap在對象{ name: 『toto』 }上只有弱引用,而這個對象已經沒有強引用了。(只有變數obj有保持引用)
何時使用Weakmap?
正如你所看到的,Weakmap可以用在任何地方
快取器函數
const cache = new WeakMap() const process = function (obj) { // 如果輸入的值不在快取器中 if (!cache.has(obj)) { // 想像一個函數需要很大的記憶體或者資源 // 當輸入相同時,我們不想重複執行bigOperation函數 const result = bigOperation(obj) // 所以此時執行一次函數並將它的結果存入快取中 cache.set(obj, result) } return cache.get(obj) } let obj = { /* any object */ } // 第一次我們沒有這個輸入作為快取,所以在第二次的時候我們才不需要執行這個函數, const firstResult = process(obj) // 只需要從快取中取出結果 const secondeResult = process(obj) // 源對象將被從weakmap中移除 obj = null
使用map,這個快取器函數應該將obj對象保存在記憶體中。
但這將導致記憶體泄漏!
當我們對一個不再使用的對象保持引用的時候將會造成記憶體泄漏,所以如果你不再使用對象,請刪除它的任何變數引用。
使用weakmap時我們不應該使用.keys() / .values() /.entries(),因為我們不知道何時垃圾回收器會移除這個對象。
最後一個例子
動態無泄漏記憶體的訪問計數器
// 訪問計數器 let visitsCountMap = new WeakMap() // 增加訪問計數 function countUser(user) { const count = visitsCountMap.get(user) || 0 visitsCountMap.set(user, count + 1) } let toto = { name: "toto" } countUser(toto) // 計算訪問次數 // 將toto對象從記憶體中移除 toto = null

