這篇文章主要介紹“Vue3中怎么用WeakMap作為緩存區(qū)”,在日常操作中,相信很多人在Vue3中怎么用WeakMap作為緩存區(qū)問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Vue3中怎么用WeakMap作為緩存區(qū)”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
創(chuàng)新互聯(lián)建站是一家專業(yè)提供南寧企業(yè)網(wǎng)站建設,專注與網(wǎng)站建設、成都做網(wǎng)站、H5技術、小程序制作等業(yè)務。10年已為南寧眾多企業(yè)、政府機構等服務。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站建設公司優(yōu)惠進行中。
在讀 Vue 3 響應式原理部分代碼的過程中看到其在進行響應式處理的時候,為每個對象使用 WeakMap
創(chuàng)建了一個「緩存區(qū)」,代碼如下:
// 注意下面這句代碼! const reactiveMap = new WeakMap(); // 核心進行劫持的方法 處理 get 和 set 的邏輯 const mutableHandlers = { get, set } function reactive(target: object) { return createReactiveObject(target, mutableHandlers, reactiveMap); } /** * @description 創(chuàng)建響應式對象 * @param {Object} target 需要被代理的目標對象 * @param {Function} baseHandlers 針對每種方式對應的不同處理函數(shù) * @param {Object} proxyMap WeakMap 對象 */ function createReactiveObject(target, baseHandlers, proxyMap) { // 檢測 target 是不是對象,不是對象直接返回,不進行代理 if (!isObject(target)) { return target } const existsProxy = proxyMap.get(target); // 如果該對象已經(jīng)被代理過了,則直接返回,不進行重復代理 if (existsProxy) { return existsProxy } // 未被代理過,則創(chuàng)建代理對象 const proxy = new Proxy(target,baseHandlers); // 緩存,避免重復代理,即避免 reactive(reactive(Object)) 的情況出現(xiàn) proxyMap.set(target,proxy); return proxy }
從上面的代碼可以看出,WeakMap
緩存區(qū)的作用就是用來防止對象被重復代理。
為什么 Vue 3 使用 WeakMap
來緩存代理對象?為什么不使用其他的方式來進行緩存,比如說 Map
?
WeakMap
對象是一組鍵值對的集合,其中的鍵是 弱引用的。其鍵必須是 對象,而值可以是任意的。
new WeakMap([iterable])
Iterable
是一個數(shù)組(二元數(shù)組)或者其他可迭代的且其元素是鍵值對的對象。每個鍵值對會被加到新的 WeakMap
里。
WeakMap
有四個方法:分別是 get
、set
、has
、delete
,下面我們看一下其大致的用法:
const wm1 = new WeakMap(), wm2 = new WeakMap(), wm3 = new WeakMap(); const o1 = {}, o2 = function() {}, o3 = window; wm1.set(o1, 37); wm1.set(o2, "azerty"); wm2.set(o1, o2); // value 可以是任意值,包括一個對象或一個函數(shù) wm2.set(o3, undefined); wm2.set(wm1, wm2); // 鍵和值可以是任意對象,甚至另外一個 WeakMap 對象 wm1.get(o2); // "azerty" wm2.get(o2); // undefined,wm2 中沒有 o2 這個鍵 wm2.get(o3); // undefined,值就是 undefined wm1.has(o2); // true wm2.has(o2); // false wm2.has(o3); // true (即使值是 undefined) wm3.set(o1, 37); wm3.get(o1); // 37 wm1.has(o1); // true wm1.delete(o1); wm1.has(o1); // false
WeakMap
而不是 Map
在 JavaScript 里,map
API 可以通過四個 API 方法共用兩個數(shù)組(一個存放鍵,一個存放值)來實現(xiàn)。這樣在給這種 map
設置值時會同時將鍵和值添加到這兩個數(shù)組的末尾。從而使得鍵和值的索引在兩個數(shù)組中相對應。當從該 map
取值的時候,需要遍歷所有的鍵,然后使用索引從存儲值的數(shù)組中檢索出相應的值。
但這樣的實現(xiàn)會有兩個很大的缺點,首先賦值和搜索操作都是 O(n)
的時間復雜度(n
是鍵值對的個數(shù)),因為這兩個操作都需要遍歷整個數(shù)組來進行匹配。
另外一個缺點是可能會導致 內(nèi)存泄漏,因為數(shù)組會一直引用著每個鍵和值。這種引用使得 垃圾回收算法不能回收處理他們,即使沒有其他任何引用存在了。
let jser = { name: "dachui" }; let array = [ jser ]; jser = null; // 覆蓋引用
上面這段代碼,我們把一個對象放入到數(shù)組中,那么只要這個數(shù)組存在,那么這個對象也就存在,即使沒有其他對該對象的引用。
let jser = { name: "dachui" }; let map = new Map(); map.set(jser, ""); jser = null; // 覆蓋引用
類似的,如果我們使用對象作為常規(guī) Map
的鍵,那么當 Map
存在時,該對象也將存在。它會占用內(nèi)存,并且不會被垃圾回收機制回收。
相比之下,原生的 WeakMap
持有的是每個鍵對象的 弱引用,這意味著在沒有其他引用存在時垃圾回收能正確進行。
正是由于這樣的弱引用,WeakMap
的 key
是不可枚舉的 (沒有方法能給出所有的 key
)。如果 key
是可枚舉的話,其列表將會受垃圾回收機制的影響,從而得到不確定的結果。因此,如果你想要這種類型對象的 key
值的列表,你應該使用 Map
。
綜上,我們可以得出以下結論:WeakMap 的鍵所指向的對象,不計入垃圾回收機制。
所以,如果你要往對象上添加數(shù)據(jù),又不想干擾垃圾回收機制,就可以使用 WeakMap
。
看到這里大家就應該知道了,Vue 3 之所以使用 WeakMap
來作為緩沖區(qū)就是為了能將 不再使用的數(shù)據(jù)進行正確的垃圾回收。
關于「弱引用」,維基百科給出了答案:
在計算機程序設計中,弱引用與 強引用相對,是指不能確保其引用的對象不會被垃圾回收器回收的引用。一個對象若只被弱引用所引用,則被認為是不可訪問(或弱可訪問)的,并因此 可能在任何時刻被回收。
那么,為什么會出現(xiàn)弱引用呢?弱引用除了能解決上述問題之外還能解決什么問題呢?要想回答這些問題,我們首先需要了解一下 V8
引擎是如何進行垃圾回收的。
對于 JSer
來說,內(nèi)存的管理是自動的、無形的,這一切都歸功于 V8
引擎在背后默默地幫我們找到不需要使用的內(nèi)存并進行清理。
那么,當我們不再需要某個東西時會發(fā)生什么,V8
引擎又是如何發(fā)現(xiàn)并清理它的呢?
現(xiàn)在各大瀏覽器通常用采用的垃圾回收有兩種方法,一種是「引用計數(shù)」,另外一種就是「標記清除」。下面我們來看一下:
標記清除被稱為 mark-and-sweep
,它是基于 可達性來判斷對象是否存活的,它會定期執(zhí)行以下「垃圾回收」步驟:
垃圾收集器找到所有的根,并標記(記?。┧鼈?。
然后它遍歷并標記來自它們的所有引用。所有被遍歷到的對象都會被記住,以免將來再次遍歷到同一個對象。
……如此操作,直到所有可達的(從根部)引用都被訪問到。
沒有被標記的對象都會被刪除。
我們還可以將這個過程想象成從根溢出一個巨大的油漆桶,它流經(jīng)所有引用并標記所有可到達的對象,然后移除未標記的。
引用計數(shù)方式最基本的形態(tài)就是讓每個被管理的對象與一個引用計數(shù)器關聯(lián)在一起,該計數(shù)器記錄著該對象當前被引用的次數(shù),每當創(chuàng)建一個新的引用指向該對象時其計數(shù)器就加 1,每當指向該對象的引用失效時計數(shù)器就減 1。當該計數(shù)器的值降到 0 就認為對象死亡。
引用計數(shù)與基于「可達性」的標記清除的內(nèi)存管理方式最大的區(qū)別就是,前者只需要 局部的信息,而后者需要 全局的信息。
在引用計數(shù)中每個計數(shù)器只記錄了其對應對象的局部信息 —— 被引用的次數(shù),而沒有(也不需要)一份全局的對象圖的生死信息。
由于只維護局部信息,所以不需要掃描全局對象圖就可以識別并釋放死對象。但也因為缺乏全局對象圖信息,所以 無法處理循環(huán)引用的狀況。
所以,更高級的引用計數(shù)實現(xiàn)會引入 弱引用的概念來打破某些已知的循環(huán)引用。
WeakMap
應用的典型場合就是以 DOM
節(jié)點作為鍵名。下面是一個例子。
const myWeakmap = newWeakMap(); myWeakmap.set( document.getElementById('logo'), { timesClicked: 0 }, ); document.getElementById('logo').addEventListener('click', () => { const logoData = myWeakmap.get(document.getElementById('logo')); logoData.timesClicked++; }, false);
上面代碼中,document.getElementById('logo')
是一個 DOM
節(jié)點,每當發(fā)生 click
事件,就更新一下狀態(tài)。我們將這個狀態(tài)作為值放在 WeakMap
里,對應的鍵就是這個節(jié)點對象。一旦這個 DOM
節(jié)點刪除,該狀態(tài)就會自動消失,不存在內(nèi)存泄漏風險。
謎底就在謎面上,文章一開頭我們提出的問題就是這里的答案。Vue 3 在實現(xiàn)響應式原理的時候就是使用了 WeakMap
來作為響應式對象的「緩存區(qū)」。
關于這一點用法也很簡單,當我們需要關聯(lián)對象和數(shù)據(jù),比如在不修改原有對象的情況下儲存某些屬性或者根據(jù)對象儲存一些計算的值等,而又不想手動去管理這些內(nèi)存問題的時候就可以使用 WeakMap
。
WeakMap
的另一個用處是部署類中的私有屬性。
值得一提的是,TypeScript 中已經(jīng)實現(xiàn)的 private
私有屬性原理就是利用 WeakMap
。
私有屬性應該是不能被外界訪問到,不能被多個實例共享,JavaScript 中約定俗成地使用下劃線來標記私有屬性和方法,一定程度來說是不靠譜的。
下面我們用三種方法來實現(xiàn):
版本一:閉包
const testFn = (function () { let data; class Test { constructor(val) { data = val } getData() { return data; } } return Test; })(); let test1 = new testFn(3); let test2 = new testFn(4); console.log(test1.getData()); // 4 console.log(test2.getData()); // 4
可以看到最后都輸出 4
,多實例共享私有屬性了,所以版本一不符合。
版本二:Symbol
const testFn = (function () { let data = Symbol('data') class Test { constructor(val) { this[data] = val } getData() { return this[data] } } return Test; })(); let test1 = new testFn(3); let test2 = new testFn(4); console.log(test1.getData()); // 3 console.log(test2.getData()); // 4 console.log(test1[Object.getOwnPropertySymbols(test1)[0]]); // 3 console.log(test2[Object.getOwnPropertySymbols(test2)[0]]); // 4
使用 Symbol
雖然實現(xiàn)了而且正確輸出了 3
、4
,但是我們發(fā)現(xiàn)可以在外界不通過 getData
方法直接拿到私有屬性,所以這種方法也不滿足我們的要求。
版本三:WeakMap
const testFn = (function () { let data = new WeakMap() class Test { constructor(val) { data.set(this, val) } getData() { return data.get(this) } } return Test; })(); let test1 = new testFn(3); let test2 = new testFn(4); console.log(test1.getData()); // 3 console.log(test2.getData()); // 4
到此,關于“Vue3中怎么用WeakMap作為緩存區(qū)”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
網(wǎng)站標題:Vue3中怎么用WeakMap作為緩存區(qū)
網(wǎng)站路徑:http://bm7419.com/article10/pssgdo.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、服務器托管、域名注冊、營銷型網(wǎng)站建設、ChatGPT、網(wǎng)站內(nèi)鏈
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)