摘要:一個對象若只被弱引用所引用,則被認為是不可訪問或弱可訪問的,并因此可能在任何時刻被回收。也就是說,一旦不再需要,里面的鍵名對象和所對應的鍵值對會自動消失,不用手動刪除引用。如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。
前言
我們先從 WeakMap 的特性說起,然后聊聊 WeakMap 的一些應用場景。
特性 1. WeakMap 只接受對象作為鍵名const map = new WeakMap(); map.set(1, 2); // TypeError: Invalid value used as weak map key map.set(null, 2); // TypeError: Invalid value used as weak map key2. WeakMap 的鍵名所引用的對象是弱引用
這句話其實讓我非常費解,我個人覺得這句話真正想表達的意思應該是:
WeakMaps hold "weak" references to key objects,
翻譯過來應該是 WeakMaps 保持了對鍵名所引用的對象的弱引用。
我們先聊聊弱引用:
在計算機程序設計中,弱引用與強引用相對,是指不能確保其引用的對象不會被垃圾回收器回收的引用。 一個對象若只被弱引用所引用,則被認為是不可訪問(或弱可訪問)的,并因此可能在任何時刻被回收。
在 JavaScript 中,一般我們創建一個對象,都是建立一個強引用:
var obj = new Object();
只有當我們手動設置 obj = null 的時候,才有可能回收 obj 所引用的對象。
而如果我們能創建一個弱引用的對象:
// 假設可以這樣創建一個 var obj = new WeakObject();
我們什么都不用做,只用靜靜的等待垃圾回收機制執行,obj 所引用的對象就會被回收。
我們再來看看這句:
WeakMaps 保持了對鍵名所引用的對象的弱引用
正常情況下,我們舉個例子:
const key = new Array(5 * 1024 * 1024); const arr = [ [key, 1] ];
使用這種方式,我們其實建立了 arr 對 key 所引用的對象(我們假設這個真正的對象叫 Obj)的強引用。
所以當你設置 key = null 時,只是去掉了 key 對 Obj 的強引用,并沒有去除 arr 對 Obj 的強引用,所以 Obj 還是不會被回收掉。
Map 類型也是類似:
let map = new Map(); let key = new Array(5 * 1024 * 1024); // 建立了 map 對 key 所引用對象的強引用 map.set(key, 1); // key = null 不會導致 key 的原引用對象被回收 key = null;
我們可以通過 Node 來證明一下這個問題:
// 允許手動執行垃圾回收機制 node --expose-gc global.gc(); // 返回 Nodejs 的內存占用情況,單位是 bytes process.memoryUsage(); // heapUsed: 4640360 ≈ 4.4M let map = new Map(); let key = new Array(5 * 1024 * 1024); map.set(key, 1); global.gc(); process.memoryUsage(); // heapUsed: 46751472 注意這里大約是 44.6M key = null; global.gc(); process.memoryUsage(); // heapUsed: 46754648 ≈ 44.6M // 這句話其實是無用的,因為 key 已經是 null 了 map.delete(key); global.gc(); process.memoryUsage(); // heapUsed: 46755856 ≈ 44.6M
如果你想要讓 Obj 被回收掉,你需要先 delete(key) 然后再 key = null:
let map = new Map(); let key = new Array(5 * 1024 * 1024); map.set(key, 1); map.delete(key); key = null;
我們依然通過 Node 證明一下:
node --expose-gc global.gc(); process.memoryUsage(); // heapUsed: 4638376 ≈ 4.4M let map = new Map(); let key = new Array(5 * 1024 * 1024); map.set(key, 1); global.gc(); process.memoryUsage(); // heapUsed: 46727816 ≈ 44.6M map.delete(key); global.gc(); process.memoryUsage(); // heapUsed: 46748352 ≈ 44.6M key = null; global.gc(); process.memoryUsage(); // heapUsed: 4808064 ≈ 4.6M
這個時候就要說到 WeakMap 了:
const wm = new WeakMap(); let key = new Array(5 * 1024 * 1024); wm.set(key, 1); key = null;
當我們設置 wm.set(key, 1) 時,其實建立了 wm 對 key 所引用的對象的弱引用,但因為 let key = new Array(5 * 1024 * 1024) 建立了 key 對所引用對象的強引用,被引用的對象并不會被回收,但是當我們設置 key = null 的時候,就只有 wm 對所引用對象的弱引用,下次垃圾回收機制執行的時候,該引用對象就會被回收掉。
我們用 Node 證明一下:
node --expose-gc global.gc(); process.memoryUsage(); // heapUsed: 4638992 ≈ 4.4M const wm = new WeakMap(); let key = new Array(5 * 1024 * 1024); wm.set(key, 1); global.gc(); process.memoryUsage(); // heapUsed: 46776176 ≈ 44.6M key = null; global.gc(); process.memoryUsage(); // heapUsed: 4800792 ≈ 4.6M
所以 WeakMap 可以幫你省掉手動刪除對象關聯數據的步驟,所以當你不能或者不想控制關聯數據的生命周期時就可以考慮使用 WeakMap。
總結這個弱引用的特性,就是 WeakMaps 保持了對鍵名所引用的對象的弱引用,即垃圾回收機制不將該引用考慮在內。只要所引用的對象的其他引用都被清除,垃圾回收機制就會釋放該對象所占用的內存。也就是說,一旦不再需要,WeakMap 里面的鍵名對象和所對應的鍵值對會自動消失,不用手動刪除引用。
也正是因為這樣的特性,WeakMap 內部有多少個成員,取決于垃圾回收機制有沒有運行,運行前后很可能成員個數是不一樣的,而垃圾回收機制何時運行是不可預測的,因此 ES6 規定 WeakMap 不可遍歷。
所以 WeakMap 不像 Map,一是沒有遍歷操作(即沒有keys()、values()和entries()方法),也沒有 size 屬性,也不支持 clear 方法,所以 WeakMap只有四個方法可用:get()、set()、has()、delete()。
應用 1. 在 DOM 對象上保存相關數據傳統使用 jQuery 的時候,我們會通過 $.data() 方法在 DOM 對象上儲存相關信息(就比如在刪除按鈕元素上儲存帖子的 ID 信息),jQuery 內部會使用一個對象管理 DOM 和對應的數據,當你將 DOM 元素刪除,DOM 對象置為空的時候,相關聯的數據并不會被刪除,你必須手動執行 $.removeData() 方法才能刪除掉相關聯的數據,WeakMap 就可以簡化這一操作:
let wm = new WeakMap(), element = document.querySelector(".element"); wm.set(element, "data"); let value = wm.get(elemet); console.log(value); // data element.parentNode.removeChild(element); element = null;2. 數據緩存
從上一個例子,我們也可以看出,當我們需要關聯對象和數據,比如在不修改原有對象的情況下儲存某些屬性或者根據對象儲存一些計算的值等,而又不想管理這些數據的死活時非常適合考慮使用 WeakMap。數據緩存就是一個非常好的例子:
const cache = new WeakMap(); function countOwnKeys(obj) { if (cache.has(obj)) { console.log("Cached"); return cache.get(obj); } else { console.log("Computed"); const count = Object.keys(obj).length; cache.set(obj, count); return count; } }3. 私有屬性
WeakMap 也可以被用于實現私有變量,不過在 ES6 中實現私有變量的方式有很多種,這只是其中一種:
const privateData = new WeakMap(); class Person { constructor(name, age) { privateData.set(this, { name: name, age: age }); } getName() { return privateData.get(this).name; } getAge() { return privateData.get(this).age; } } export default Person;ES6 系列
ES6 系列目錄地址:https://github.com/mqyqingfeng/Blog
ES6 系列預計寫二十篇左右,旨在加深 ES6 部分知識點的理解,重點講解塊級作用域、標簽模板、箭頭函數、Symbol、Set、Map 以及 Promise 的模擬實現、模塊加載方案、異步處理等內容。
如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。如果喜歡或者有所啟發,歡迎 star,對作者也是一種鼓勵。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96299.html
摘要:前言在閱讀入門的時候,零散的看到有私有變量的實現,所以在此總結一篇。構造函數應該只做對象初始化的事情,現在為了實現私有變量,必須包含部分方法的實現,代碼組織上略不清晰。 前言 在閱讀 《ECMAScript 6 入門》的時候,零散的看到有私有變量的實現,所以在此總結一篇。 1. 約定 實現 class Example { constructor() { this...
摘要:返回一個布爾值,表示該值是否為的成員。清除所有成員,沒有返回值。返回的都是遍歷器對象。結構的實例的方法,用于對每個成員執行某種操作,沒有返回值。這個特點意味著,無法引用的成員,因此是不可遍歷的。數組成員是一個或多個表示鍵值對的一維數組。 本文字數:4700+,閱讀時間約10分鐘。 如果有理解不到位的地方,歡迎大家糾錯。 一、Set 【01】Set是一種數據結構。類似于數組,但是成員的值...
摘要:事實上,不僅僅是數組,任何具有接口且每個成員都是一個雙元素的數組的數據結構都可以當做構造函數的參數。返回所有成員的遍歷器需要特別注意的是,的遍歷順序就是插入順序。轉為轉為,正常情況下,所有鍵名都是字符串。 Map JavaScript 的對象(Object),本質上是鍵值對的集合(Hash 結構),但是傳統上只能用字符串當作鍵。這給它的使用帶來了很大的限制。為了解決這個問題,ES6 提...
摘要:今天這篇文章我們來看看一道必會面試題,即如何實現一個深拷貝。木易楊注意這里使用上面測試用例測試一下一個簡單的深拷貝就完成了,但是這個實現還存在很多問題。 引言 上篇文章詳細介紹了淺拷貝 Object.assign,并對其進行了模擬實現,在實現的過程中,介紹了很多基礎知識。今天這篇文章我們來看看一道必會面試題,即如何實現一個深拷貝。本文會詳細介紹對象、數組、循環引用、引用丟失、Symbo...
Set有對應的WeakSet, Map也有WeakMap。這一篇,我們就來學習一下WeakMap有寫什么特性。先來看一下WeakMap的基本特性: 1: WeakMap是一種存儲多個鍵值對的無序列表 2: WeakMap的鍵必須是非null的對象類型 3: WeakMap的鍵對應的值,可以是任意類型 接下來看一下WeakMap的接口方法:一:WeakMap的新建與初始化與Map相同的,WeakM...
閱讀 1037·2023-04-25 17:51
閱讀 2852·2021-11-23 09:51
閱讀 1470·2021-11-08 13:21
閱讀 2428·2021-09-22 15:14
閱讀 1515·2019-08-30 12:48
閱讀 1076·2019-08-29 12:44
閱讀 1138·2019-08-26 12:21
閱讀 1396·2019-08-26 10:47