亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

ES6+ WeakMap

1. 前言

前面我們已經學了 Set 和對應的 WeakSet,Map 對應也有 WeakMap,在學習 WeakSet 時我們已經接觸到弱引的相關知識,本節我們將結合 WeakMap 深入的理解弱引用的相關問題。

由前面學到的 Set 和 WeakSet 具有很多相似的地方,比如它們存放的都是獨一無二的元素。所以,對 Map 和 WeakMap 也可以進行類比,WeakMap 中也存放的是鍵值對。不同的是 WeakMap 的 key 只能是對象,值可以是任意類型的,和 WeakSet 一樣 WeakMap 對 key 的引用是弱引用。

2. WeakMap 基本用法

WeakMap 像 Map 一樣可以接受一個二維數組進行初始化。

var wm = new WeakMap([
  [{name: 'imooc'}, 'imooc'],
  [{name: 'lesson'}, 'ES6 Wiki']
])
console.log(wm)

上面的代碼打印結果如下:

圖片描述

從打印的結果可以大概了解 WeakMap 的存儲方式,WeakMap 的實例本來就是一個對象。

WeakMap 只提供了四個方法用于操作數據。

方法名 描述
set 接收鍵值對,向 WeakMap 實例中添加元素
get 傳入指定的 key 獲取 WeakMap 實例上的值
has 傳入指定的 key 查找在 WeakMap 實例中是否存在
delete 傳入指定的 key 刪除 WeakMap 實例中對應的值

看如下實例:

var wm1 = new WeakMap();
var wm2 = new WeakMap();
var wm3 = new WeakMap();

var o1 = {name: 'imooc'};
var o2 = function(){};
var o3 = window;

// 使用 set 方法添加元素,value 可以是任意值,包括對象、函數甚至另外一個WeakMap對象
wm1.set(o1, 'ES6 Wiki');
wm1.set(o2, 10);
wm2.set(o1, o2);
wm2.set(o3, null);
wm2.set(wm1, wm2);

wm1.get(o2); // 10
wm2.get(o2); // undefined,wm2 中沒有 o2 這個鍵
wm2.get(o3); // null

wm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (即使值是null)

wm3.set(o1, 'lesson is ES6 Wiki!');
wm3.get(o1); // lesson is ES6 Wiki!

wm1.has(o1);   // true
wm1.delete(o1);
wm1.has(o1);   // false

上面的實例基本涵蓋了 WeakMap 四種方法的基本使用情況,上面也提到了 WeakMap 的 key 只能是對象類型的,如果 WeakMap 的 key 是基本類型數據時就會報錯。

var wm = new WeakMap();
wm.set('lesson', 'ES6 Wiki');
// Uncaught TypeError: Invalid錯誤value used as weak map key

上面代碼中在設置 wm 值時,報錯了。從報錯類型知道是一個類型錯誤,弱引用映射的鍵是無效的。

3. WeakMap 使用場景

上節我們學習了 Map 的使用,在 JavaScript 中對對象的引用都是強保留的,這意味著只要持有該對象的引用,垃圾回收機制就不會回收該對象。

var obj = {a: 10, b: 88};

上面是一個字面量對象,只要我們訪問 obj 對象,或者任何地方有引用該對象,這個對象就不會被垃圾回收。而在 ES6 之前 JavaScript 中沒有弱引用概念,弱引用的本質上就是不會影響垃圾回收機制。其實,WeakMap 并不是真正意義上的弱引用,只要鍵仍然存在,它就強引用其上的內容。WeakMap 僅在鍵被垃圾回收之后,才弱引用它的內容,所以也不用太糾結其中的弱。

在官方上對為什么使用 WeakMap 做了描述,Map 在存儲值是有順序的,這種順序是通過二維數組的形式來完成的。我們知道 Map 在初始化時接受一個數組,數組中的每一項也是一個數組,這個數組中包含兩個值,一個存放的是鍵,一個存放的是值。新添加的值會添加到數組的末尾,從而使得鍵值具有索引的含義。在取值時就需要進行遍歷,通過索引取出對應的值。

但是這樣存在兩個很大的缺陷:

  1. 賦值和搜索的時間復雜度都是 O (n) (n 是鍵值對的個數),因為這兩個操作都是要遍歷整個數組才能完成的;
  2. 可能會導致內存泄漏,因為數組會一直引用每個鍵和值。這種引用使得垃圾回收算法不能回收處理它們,即使沒有任何引用存在。

相比之下,原生的 WeakMap 持有的是 “弱引用”,這意味著它不會影響垃圾回收。WeakMap 中的 key 只有在鍵值存在的情況才會引用,而且只是一個讀取操作,并不會對引用的值產生影響。也正因為這樣的弱引用關系,導致 WeakMap 中的 key 是不可枚舉的,假設 key 是可枚舉的,就會對該值產生引用關系,影響垃圾回收。

如果只是單純地向對象上添加值用于檢查某些邏輯判斷,又不想影響垃圾回收機制,這個時候就可以使用 WeakMap。這里說一點,在一些框架中已經使用了像 WeakMap 和 WeakSet 這樣的數據結構,其中 Vue3 就引入了這樣的新數據進行一些必要的邏輯判斷,有興趣的可以去扒扒 Vue3 的源碼研究研究。

4. 總結

本節主要介紹了 WeakMap 的使用和應用場景,這里要說明的一點是:WeakMap 不算真正意義上的弱引用方式,只要鍵仍然存在,它就強引用其上的內容。最新的 ES 方案提出了 WeakRef 的 API 作為真正的弱引用方式,現在還處于不穩定期間,也還存在一些問題,如果有興趣的可以研究一下。最后,在 WeakMap 的使用上,大多數都是用來進行一些必要的邏輯判斷的。在 WeakMap 實例上添加一個對已知對象的引用,從而在需要使用時,對該對象進行必要的邏輯判斷。