ES6+ WeakSet
1. 前言
上一節(jié)我們學(xué)習(xí)了 Set 數(shù)據(jù)結(jié)構(gòu),本節(jié)將學(xué)習(xí)與它類似的數(shù)據(jù)結(jié)構(gòu) WeakSet,不同的是 WeakSet 存放的數(shù)據(jù)是一個(gè)弱引用類型。在 JavaScript 中,對(duì)象的引用是強(qiáng)保留的,這意味著只要持有對(duì)象的引用,它就不會(huì)被垃圾回收。JavaScript 屬于高級(jí)語言, 存在 GC 機(jī)制,不需要直接地去操作內(nèi)存,避免了很多問題。同時(shí)也讓一些內(nèi)存泄漏的問題變得更加不易察覺,所以 ES6 引入了 WeakSet 和 WeakMap 這樣存儲(chǔ)弱引用類型的數(shù)據(jù)結(jié)構(gòu),是不會(huì)阻止它被垃圾回收的。
2. 基本用法
WeakSet 對(duì)象允許你將一個(gè)弱引用對(duì)象報(bào)錯(cuò)在一個(gè)集合中。和 Set 一樣,它們都是構(gòu)造函數(shù),都需要實(shí)例化才能使用。WeakSet 可以接收一個(gè)可迭代對(duì)象作為參數(shù),則該對(duì)象的所有迭代值都會(huì)被自動(dòng)添加進(jìn)生成的 WeakSet
對(duì)象中。null 被認(rèn)為是 undefined。
const ws = new WeakSet([iterable]);
WeakSet 對(duì)數(shù)據(jù)的操作方法相對(duì) Set 是比較少的,只有添加、刪除和查找,而且不能被遍歷。
方法名 | 描述 |
---|---|
add | 向 WeakSet 實(shí)例中添加值 |
delete | 刪除 WeakSet 實(shí)例中的指定值 |
has | 查找指定的值是否在 WeakSet 實(shí)例中 |
WeakSet 存放的一般都是對(duì)象的引用,如下實(shí)例:
var ws = new WeakSet();
var obj1 = {};
var obj2 = {};
ws.add(obj1);
ws.add(obj2);
ws.has(obj1); // true
ws.has(obj2); // true
ws.delete(obj1); // 從 set 中刪除 obj1 對(duì)象
ws.has(obj1); // false, obj1 對(duì)象已經(jīng)被刪除了
ws.has(obj2); // true, obj2 依然存在
3. 對(duì)比 Set
WeakSet
對(duì)象是一些對(duì)象值的集合,并且其中的每個(gè)對(duì)象值都只能出現(xiàn)一次。在 WeakSet
的集合中是唯一的,這和 Set 對(duì)象是一樣的。
但 WeakSet
和 Set
還是有明顯的區(qū)別的,主要區(qū)別有兩點(diǎn):
- 與
Set
相比,WeakSet
只能是對(duì)象的集合,而不能是任何類型的任意值,這個(gè) Set 可以存儲(chǔ)任何類型的值不同; WeakSet
是弱引用:WeakSet
對(duì)象中存儲(chǔ)的對(duì)象值都是被弱引用的,如果沒有其他的變量或?qū)傩砸眠@個(gè)對(duì)象值,則這個(gè)對(duì)象值會(huì)被當(dāng)成垃圾回收掉。正因?yàn)檫@樣,WeakSet
對(duì)象是無法被枚舉的,沒有辦法拿到它包含的所有元素。
當(dāng)我們?nèi)ト∫粋€(gè)對(duì)象進(jìn)行操作時(shí),外界必然存在對(duì)這個(gè)對(duì)象的引用,否則我們不可能取到這個(gè)對(duì)象。而弱引用到底是做什么的呢?
首先我們要知道對(duì)象的生命周期是,只有對(duì)象存在引用就不會(huì)被 GC 回收。但有時(shí)候我們只是需要這個(gè)集合去判斷一些邏輯,如果使用 Set 對(duì)象的話,就會(huì)存在引用,這樣實(shí)例化的內(nèi)容就不會(huì)被回收。這時(shí),使用 WeakMap 就是有必要的事了。讓我們來看下面的這個(gè)實(shí)例:
const requests = new WeakSet();
class ApiRequest {
constructor() {
requests.add(this);
}
makeRequest() {
if(!request.has(this)) throw new Error("Invalid access");
// do work
}
}
上面的代碼中,ApiRequest 想驗(yàn)證一下 this 的來源,使用 WeakMap 來存儲(chǔ)這個(gè) this 對(duì)象,并在 makeRequest 執(zhí)行時(shí)去驗(yàn)證一下是否是 ApiRequest 這個(gè)類。這里的 requests 實(shí)例并不想?yún)⑴c到 ApiRequest 類中的生命周期中去,它只是作為一個(gè)條件判斷使用的。如果使用 Set 對(duì)象的話,這個(gè)實(shí)例就會(huì)在 ApiRequest 類中存在引用關(guān)系,并一直保存在實(shí)例中,增加內(nèi)存的開銷,也可能會(huì)發(fā)生內(nèi)存泄漏。而使用 WeakMap 則不同它是弱引用,只有在劫持的時(shí)候才會(huì)被獲取到。
4. 小結(jié)
本節(jié)學(xué)習(xí)了 WeakMap 對(duì)象,它用了存儲(chǔ)對(duì)象的值,存儲(chǔ)的值都是弱引用類型。其實(shí),弱引用就是將對(duì)象鍵添加到 WeakSet 上,而 WeakSet 對(duì)對(duì)象的引用不會(huì)影響垃圾回收,也就是說,你持有對(duì)象的引用,就可以獲取元數(shù)據(jù)。一旦不再持有對(duì)象的引用,即使你仍持有并添加了該對(duì)象的引用,也會(huì)被垃圾回收。