WeakMap與WeakSet的本質是,向一些「不屬於你的對象」,添加(attach)一些私有數據。node
這裏的不屬於你,指的是ownership,若是這個對象不是你建立的,也不是專門爲你建立的,你沒法掌控,那麼這個對象就「不屬於你」。typescript
固然,koa提供了ctx,就是爲了讓middleware添加數據,這個成爲了全部開發者的共識,因此只要注意避免命名衝突,通常往ctx添加屬性沒有什麼問題。
如上所說,之前咱們的一個壞習慣是往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的優點:模塊化
對於myWeakMap.set(domNode,bigDataObj)
,WeakMap僅僅保持對domNode的弱引用,而不妨礙它被垃圾收集。之前的domNode._data = myBigDataObj
這種方式,其實也不妨礙domNode被垃圾收集。在垃圾收集方面,兩種方式效果相同。
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來代替它。