Vue3中怎么用WeakMap作為緩存區(qū)

這篇文章主要介紹“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)惠進行中。

Vue3中怎么用WeakMap作為緩存區(qū)

在讀 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

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 持有的是每個鍵對象的 弱引用,這意味著在沒有其他引用存在時垃圾回收能正確進行。

正是由于這樣的弱引用,WeakMapkey 是不可枚舉的 (沒有方法能給出所有的 key)。如果 key 是可枚舉的話,其列表將會受垃圾回收機制的影響,從而得到不確定的結果。因此,如果你想要這種類型對象的 key 值的列表,你應該使用 Map。

綜上,我們可以得出以下結論:WeakMap 的鍵所指向的對象,不計入垃圾回收機制

所以,如果你要往對象上添加數(shù)據(jù),又不想干擾垃圾回收機制,就可以使用 WeakMap。

看到這里大家就應該知道了,Vue 3 之所以使用 WeakMap 來作為緩沖區(qū)就是為了能將 不再使用的數(shù)據(jù)進行正確的垃圾回收。

什么是弱引用

關于「弱引用」,維基百科給出了答案:

在計算機程序設計中,弱引用強引用相對,是指不能確保其引用的對象不會被垃圾回收器回收的引用。一個對象若只被弱引用所引用,則被認為是不可訪問(或弱可訪問)的,并因此 可能在任何時刻被回收。

為什么會出現(xiàn)弱引用

那么,為什么會出現(xiàn)弱引用呢?弱引用除了能解決上述問題之外還能解決什么問題呢?要想回答這些問題,我們首先需要了解一下 V8 引擎是如何進行垃圾回收的。

對于 JSer 來說,內(nèi)存的管理是自動的、無形的,這一切都歸功于 V8 引擎在背后默默地幫我們找到不需要使用的內(nèi)存并進行清理。

那么,當我們不再需要某個東西時會發(fā)生什么,V8 引擎又是如何發(fā)現(xiàn)并清理它的呢?

現(xiàn)在各大瀏覽器通常用采用的垃圾回收有兩種方法,一種是「引用計數(shù)」,另外一種就是「標記清除」。下面我們來看一下:

標記清除

標記清除被稱為 mark-and-sweep,它是基于 可達性來判斷對象是否存活的,它會定期執(zhí)行以下「垃圾回收」步驟:

  • 垃圾收集器找到所有的根,并標記(記?。┧鼈?。

  • 然后它遍歷并標記來自它們的所有引用。所有被遍歷到的對象都會被記住,以免將來再次遍歷到同一個對象。

  • ……如此操作,直到所有可達的(從根部)引用都被訪問到。

  • 沒有被標記的對象都會被刪除。

我們還可以將這個過程想象成從根溢出一個巨大的油漆桶,它流經(jīng)所有引用并標記所有可到達的對象,然后移除未標記的。

引用計數(shù)

引用計數(shù)方式最基本的形態(tài)就是讓每個被管理的對象與一個引用計數(shù)器關聯(lián)在一起,該計數(shù)器記錄著該對象當前被引用的次數(shù),每當創(chuàng)建一個新的引用指向該對象時其計數(shù)器就加 1,每當指向該對象的引用失效時計數(shù)器就減 1。當該計數(shù)器的值降到 0 就認為對象死亡。

區(qū)別

引用計數(shù)與基于「可達性」的標記清除的內(nèi)存管理方式最大的區(qū)別就是,前者只需要 局部的信息,而后者需要 全局的信息。

在引用計數(shù)中每個計數(shù)器只記錄了其對應對象的局部信息 —— 被引用的次數(shù),而沒有(也不需要)一份全局的對象圖的生死信息。

由于只維護局部信息,所以不需要掃描全局對象圖就可以識別并釋放死對象。但也因為缺乏全局對象圖信息,所以 無法處理循環(huán)引用的狀況。

所以,更高級的引用計數(shù)實現(xiàn)會引入 弱引用的概念來打破某些已知的循環(huán)引用。

WeakMap 應用

存儲 DOM 節(jié)點

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)存泄漏風險。

數(shù)據(jù)緩存

謎底就在謎面上,文章一開頭我們提出的問題就是這里的答案。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)

搜索引擎優(yōu)化