WeakSet 不防止内存泄漏,仅不强持有 DOM 节点,节点无其他引用时自动释放;它仅适用于“是否见过”的布尔标记,不可存数据或状态,且不支持遍历与清理操作。

WeakSet 本身不“防止”内存泄漏,它只是不制造泄漏——它不强持有 DOM 节点,节点被移除且无其他引用时,会自然释放,WeakSet 中的记录也同步失效。关键不在 WeakSet 怎么用,而在别让其他地方偷偷留住节点。
只用 WeakSet 做布尔标记,不存数据
WeakSet 的定位很明确:它不是缓存,也不是状态容器,而是轻量级的“是否见过你”标记。
- ✅ 合适场景:判断某个按钮是否已初始化、某个元素是否绑过事件、某个卡片是否加载过组件
- ❌ 不合适场景:存点击次数、配置对象、异步 pending 状态——这些该用 WeakMap
- 用法极简:
-
const seen = new WeakSet() -
seen.add(node)(注意:node 必须是真实 DOM 元素,传 null 或字符串会报错) -
if (!seen.has(node)) { init(node); seen.add(node); }
-
- 没有
delete()、clear()、size,也不支持遍历——这不是缺陷,是设计使然,避免误操作延长生命周期
节点不释放?问题一定在 WeakSet 外面
WeakSet 从不阻止回收,但以下常见情况会让节点继续驻留:
- 给节点绑了
addEventListener却没removeEventListener→ 事件系统强持有 - 把同一节点存进了
Map、普通对象或全局变量 → 强引用成了“锚点” - 在闭包里捕获了节点,比如
function handler() { console.log(node); } - 动态创建节点后赋值给模块级变量或
window.xxx
只要存在任一强引用,WeakSet 就形同虚设。它只解决自己这一路的引用,管不了别的。
搭配事件委托,真正实现零清理负担
动态页面中,节点频繁增删,手动维护集合极易遗漏。WeakSet 的优势在此体现:
- 不需要监听
MutationObserver - 不需要在节点移除时调用
delete() - 不需要定时轮询清理
例如:const inited = new WeakSet(); function setupButton(btn) { if (inited.has(btn)) return; btn.addEventListener('click', handleAction); inited.add(btn); } // 页面任意处插入新按钮,直接调用 setupButton // 按钮 later 被 remove() 或 innerHTML 清空 → 只要没其他强引用,内存自动归还
WeakSet 是工具,不是保险丝。它让内存管理更可信,前提是开发者清楚每一处引用来源。

















