weakset防内存泄漏的核心在于弱引用:它不阻止dom节点被垃圾回收,节点移除且无其他强引用时自动释放,适合仅作“是否处理过”的布尔标记,不支持遍历、size或clear。

WeakSet 不是“黑科技”,而是专为这类场景设计的轻量工具:它不阻止 DOM 节点被垃圾回收,只要节点从页面移除且没有其他强引用,内存就会自动释放——你不用手动清理,也不用监听节点销毁事件。
WeakSet 为什么能防内存泄漏
普通 Set 或数组会强引用所存的 DOM 节点,哪怕节点已被 remove() 或父元素清空,只要还在集合里,GC 就不敢动它。WeakSet 则不同:它对节点的引用是弱引用,不计入 GC 的“可达性判断”。节点一旦脱离 DOM、又没被事件监听器、闭包、全局变量等持有,就会立刻被回收,WeakSet 内部记录也同步失效。
正确用法:只做“是否处理过”的标记
WeakSet 最适合的用途是布尔型状态标记,比如“这个节点初始化过了吗”“这个按钮已绑定事件了吗”。它不支持遍历、查 size、删指定项,这恰恰是安全性的保障。
- 初始化:const processed = new WeakSet()
- 添加节点:processed.add(document.getElementById('btn'))(注意:传 null 或原始值会报错)
- 检查状态:if (!processed.has(el)) { init(el); processed.add(el); }
- 无需 clear()、delete() 或定时清理——节点消失后,has() 自然返回 false
哪些情况会让 WeakSet “失效”
WeakSet 本身没问题,但常见错误会让节点继续驻留内存:
- 节点绑了 addEventListener 却没解绑 → 事件系统强持有该节点
- 用 Map 或普通对象同时存了同一节点 → Map 的强引用成了“锚点”
- 在闭包中捕获了节点,例如 function makeHandler(el) { return () => el.click(); }
- 把节点赋给了 window.xxx 或模块级变量
WeakSet vs WeakMap:怎么选
如果只是打个标、记个“是/否”,用 WeakSet;如果要存复杂数据(如点击次数、配置对象、异步状态),就该用 WeakMap:
- WeakSet:无键值概念,纯集合,适合 observedNodes.has(node)
- WeakMap:以节点为 key,任意值为 value,适合 nodeData.set(node, { pending: true })
- 两者都不支持遍历和 size,这不是缺陷,而是弱引用机制的必然约束










