先看一個例子
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
是一個額外的資料儲存,它可以讓我們從外部(第三方庫)擴充套件或者封裝一個物件,而不需要進行垃圾回收的推斷,或者能夠智慧的建立一個快取函數。
不用擔心看不明白,在比較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
可以用在任何地方
快取器函數
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
本文來自部落格園,作者:喆星高照,轉載請註明原文連結:https://www.cnblogs.com/houxianzhou/p/16384815.html