Use WeakMap for mappings of once listeners and their wrappers
momocow opened this issue · comments
在現在的做法中
一次性的監聽器實際上是先經過一層封裝 (為了自動移除)
而後才註冊到 event map 中
因此透過一個 Map 對象負責配對原先的監聽器到封裝後的方法
用以輔助 #off(event, listener)
找到正確的封裝方法
並將其移除。
若用戶未主動呼叫#off()
方法, 或者並未有呼叫#off()
方法的需求,
內存中仍有這個 Map 對象配對, 增加 memory leak 風險。
因此改用 WeakMap 可提升這部分的性能,
在現有的慣例中,
所有需要呼叫#off()
方法的用戶自然會握有監聽器的參照,
沒握有參照的就是不需要呼叫#off()
方法。
這不是流程圖, 這是當用戶呼叫 #once(event, listener)
時閉包參照的示意圖
藍色兩條是 #once()
中的局部變數, #once()
結束後會消失
我們給 onceWrapper
這個變數指定一個方法(圖中的 once listener )作為值,
這個方法做兩件事情:
- 執行
listener
, 所以它擁有一條通往listener
的參照 (綠色) - 執行完畢, 將整個
onceWrapper
自 event map 中移除, 因此它也擁有自己的參照 (橘色)
#once()
結束前會將 onceWrapper
註冊到 event map 中,
因此在 event map 留下紅色參照。
今天有兩個狀況
- 用戶未保留 listener 參照, 任 listener 執行一次後自己消失
- listener 尚未執行, 便被用戶呼叫
#off(event, listener)
移除
前者 listener 可以順利被執行的關鍵是紅色參照, 唯一使 listener 連結外部的參照
紅色參照在執行完畢後, 會自行呼叫 #off()
斷開這條參照
後者關鍵除了紅色參照, 尚有用戶自行保有的參照
當用戶呼叫#off()
斷開紅色參照,
只須讓握有的參照也失效即可 (如將該方法變量指向null)