WeakMap和WeakSet通过弱引用避免内存泄漏,适用于缓存和元数据绑定;WeakMap以对象为键存储私有状态,WeakSet用于对象存在性标记,二者均不阻止垃圾回收。

WeakMap 和 WeakSet 能帮助 JavaScript 应用减少内存泄漏风险,尤其适合缓存、元数据绑定等场景。它们的核心价值不在于“存得多”,而在于“不阻止垃圾回收”——只要键(WeakMap)或值(WeakSet)本身被其他地方释放,它们就会自动从集合中消失。
WeakMap:用对象作键的“临时附着式存储”
WeakMap 的键必须是对象,且对键是弱引用。这意味着:如果某个对象只作为 WeakMap 的键存在,而 elsewhere 没有其他引用,该对象就能被正常回收,WeakMap 内部条目也会随之清理。
- 典型用途:为第三方对象(如 DOM 元素、类实例)附加私有状态,又不想干扰其生命周期
- 不能遍历、没有 size 属性、不支持 clear() —— 这不是缺陷,而是设计使然,确保无法意外延长对象存活时间
- 示例:给按钮添加点击次数统计,不需手动清理
button.addEventListener('click', () => {
const count = (clickCount.get(button) || 0) + 1;
clickCount.set(button, count);
console.log(`已点 ${count} 次`);
});
// button 被移除 DOM 后,其对应记录自动失效
WeakSet:轻量级的对象“存在性标记”
WeakSet 只能存对象,同样使用弱引用。它适合做布尔型标记:某个对象是否被处理过、是否在监听列表中、是否参与某次计算等。
- 无迭代器、无 size、不可 forEach —— 仅提供 add()、has()、delete()
- 比用 Map/obj 做标记更安全:避免因忘记清理导致对象长期驻留内存
- 常见于防重复处理逻辑,比如防止同一元素被多次初始化
function initElement(el) {
if (initialized.has(el)) return;
// 执行初始化逻辑
initialized.add(el);
}
什么时候不该用?明确 Weak 的边界
WeakMap/WeakSet 不是万能缓存方案。它们解决的是“引用导致的泄漏”,而非“业务逻辑需要的持久缓存”。
- 不要指望用 WeakMap 存配置、常量或字符串——键只能是对象,字符串会报错
- 不能靠 WeakSet 判断“某个值是否还在用”,因为它不保证实时同步(GC 时机不可控)
- 调试困难:无法 console.log 查看全部内容,也不支持序列化
- 若需可靠缓存,请搭配 LRU 策略的 Map 或专用缓存库,并主动管理生命周期
配合现代 API,发挥协同效应
WeakMap/WeakSet 在与 Proxy、FinalizationRegistry 等配合时,能构建更健壮的资源管理模式。
- 用 WeakMap 存储 Proxy 的 handler 上下文,避免 handler 持有目标对象形成循环引用
- WeakMap + FinalizationRegistry 可实现“对象销毁时回调”,用于清理外部资源(如 Canvas 渲染上下文、WebSocket 连接)
- React/Vue 等框架内部大量使用 WeakMap 实现组件私有状态隔离,正是看中其自动清理能力
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











