JavaScript的WeakMap:向【不屬於你的對象】添加【私有數據】

WeakMap與WeakSet的本質是,向一些「不屬於你的對象」,添加(attach)一些私有數據。node

ownership: 這個對象屬於你嗎?

這裏的不屬於你,指的是ownership,若是這個對象不是你建立的,也不是專門爲你建立的,你沒法掌控,那麼這個對象就「不屬於你」。typescript

  • 例子1:dom對象,是由瀏覽器建立的,而且它會被你沒法掌控的代碼使用(好比大部分頁面的dom由React框架管理,又或者當前頁面同時運行了別人編寫的JavaScript),那麼你就不是這個對象的擁有者,應該避免往上面添加屬性。
  • 例子2:koa框架暴露的request、response對象。若是你正在編寫一個koa middleware,那麼你接收到的request、response對象就不屬於你。你應該避免向添加屬性,無心間形成信息泄露、或者鍵名衝突。
固然,koa提供了ctx,就是爲了讓middleware添加數據,這個成爲了全部開發者的共識,因此只要注意避免命名衝突,通常往ctx添加屬性沒有什麼問題。

WeakMap爲什麼出現

如上所說,之前咱們的一個壞習慣是往domNode對象添加一些自用的屬性:domNode._data = myBigDataObj,來記錄一些信息,以便之後讀取。瀏覽器

後來咱們爲了不衝突,以symbol做爲key,給對象添加屬性,確保別人訪問不到這個屬性:domNode[mySymbol] = myBigDataObj數據結構

雖然實現了私有屬性,可是typescript會對這種代碼報錯,由於mySymbol並不在Node的Interface上。框架

WeakMap則是另外一種實現私有屬性的方式。尤爲擅長向你不擁有的對象附加一些信息
WeakMap的使用方式與普通Map幾乎相同,只不過它的key是對象,而且一個對象在WeakMap中做爲key,不會阻止它被垃圾回收。好比:dom

const attachedData = new WeakMap();
let keyObj = {name:'keyObj'};
let dataObj = {name:'dataObj'};
attachedData.set(keyObj, dataObj);
// 刪掉對dataObj的引用
dataObj = null;
// dataObj實際還在內存中,沒有被垃圾回收,由於它還能夠經過WeakMap+key訪問到:
console.log(attachedData.get(keyObj));
// 刪掉對keyObj的引用
keyObj = null;
// keyObj、dataObj都已經被垃圾回收了,由於沒有任何方式能夠訪問到它們

再舉一個實踐中的例子,myWeakMap.set(domNode,bigDataObj),將一個對象與domNode關聯起來。之後再次遇到domNode的時候,咱們能夠通myWeakMap.get(domNode)拿到這個對象。若是這個domNode被清除、垃圾收集,那麼myWeakMap會自動取消這條關聯記錄,若是其餘地方沒有保留bigDataObj的引用,那麼它也會被垃圾收集。
在這個例子中,Node就是一把「鑰匙」,只要鑰匙沒有弄丟,你就能夠用它來拿到myWeakMap中存儲的對應數據。若是你的鑰匙丟了,myWeakMap會自動刪掉這條關聯信息(bigDataObj引用數減一)。由於若是連鑰匙都丟了,你不可能再向myWeakMap索要這條數據,這條關聯信息也就不可能再須要用到。koa

使用WeakMap的優點:模塊化

  • 封裝性。外部代碼沒法拿這些信息,甚至連對象本來的擁有者都不行!更不可能會出現衝突。
  • 代碼模塊化。建立weakmap、使用weakmap的代碼,均可以維護在一個獨立的模塊中,而不會影響到對象本來的初始化代碼(本來的初始化代碼不會意識到這個新的私有屬性的存在)。
  • typescript也不會報錯,你無需extend原來的Interface,或者經過as any來繞過ts報錯。在這一點上優於Symbol的方案
對於 myWeakMap.set(domNode,bigDataObj),WeakMap僅僅保持對domNode的弱引用,而不妨礙它被垃圾收集。之前的 domNode._data = myBigDataObj這種方式,其實也不妨礙domNode被垃圾收集。在垃圾收集方面,兩種方式效果相同。

WeakSet

WeakSet相似WeakMap,只不過它至關於value總爲true的WeakMap,至關於給一個對象打上標記。code

好比當有一個圖數據結構,咱們不但願重複處理同一個節點,之前可能會這樣作:node._visited= true
可是若是這個圖數據結構不屬於你(你不是這個對象的擁有者),張三的代碼也要處理這個圖結構,那麼你往上面添加屬性,可能會破壞封裝性,形成命名衝突。
後來,咱們發明了Symbol的方式:node[mySymbol]= true
再後來,咱們經過visitedWeakMap.set(node, true)來打上標記。
再後來,咱們發現map的標記值老是爲true,因而能夠簡化爲使用set:
visitedWeakSet.add(node).對象

瀏覽器兼容性

WeakMap的瀏覽器兼容性比我想象中的要好,竟然被IE11支持。
WeakSet的瀏覽器兼容性則差一些,好在咱們老是能用WeakMap來代替它。

擴展資料

相關文章
相關標籤/搜索