weakmap 与 map 的核心差异在于键的引用强度:weakmap 键为弱引用,不阻止垃圾回收,仅接受对象且不可遍历;map 键为强引用,支持任意类型,会阻碍 gc 导致内存泄漏。

WeakMap 和 Map 的核心差异不在 API 表面,而在于内存管理逻辑——WeakMap 的键是弱引用,不阻碍垃圾回收;Map 的键是强引用,会拖住对象不让它被回收。这直接决定了它们在防止内存泄漏上的能力边界。
键的类型和引用强度决定生命周期
Map 允许任意类型作键(字符串、数字、对象、Symbol),所有键都被强持有。哪怕你把一个 DOM 元素存进 Map 作为键,之后从页面移除了它,只要 Map 还拿着这个键,该元素就无法被 GC 回收,内存就一直占着。
WeakMap 只接受对象作键(null 也不行),而且这个“接受”是带条件的:它不增加该对象的引用计数。换句话说,WeakMap 对键的引用“不算数”,GC 看不到它。只要这个对象在其他地方没了任何强引用,哪怕 WeakMap 里还记着它,它也会被立刻回收,对应的键值对自动消失。
- Map 键 = 强引用 → 拖住对象,延长生命周期
- WeakMap 键 = 弱引用 → 不参与引用计数,不干预 GC 决策
- WeakMap 值仍是强引用,只弱的是键
不可遍历、无 size,是弱引用的必然代价
因为 WeakMap 的键随时可能被 GC 清掉,它的内部状态是“动态不可知”的。你不能用 for...of 遍历它,也不能调用 size 属性或 clear()、keys()、values() 等方法——这些操作都要求键集合稳定可枚举,而 WeakMap 不满足这个前提。
它的 API 极简:set(key, value)、get(key)、has(key)、delete(key)。其中 get 和 has 返回 undefined 并不一定代表没设过,也可能键已被回收。
典型场景:DOM 状态绑定与私有元数据
比如给每个按钮加一个点击次数计数器,又不想污染 DOM 元素本身:
- 用 Map:按钮被 removeChild 删除后,Map 里还存着它的引用,计数器数据和按钮本体都滞留内存
- 用 WeakMap:按钮一被移除,外部再无强引用,GC 立刻回收按钮,WeakMap 自动丢弃对应条目,干净利落
同理,框架如 Vue 3 的响应式系统用 WeakMap 存储 reactive 对象与其依赖关系(effect),确保响应式对象销毁时,相关副作用函数也能被释放,避免“监听残留”导致的泄漏。
不是万能解药,但关键时候不可替代
WeakMap 不适合做通用缓存或需要主动清理的场景——你没法知道它还剩多少项,也不能批量清空。但它天生适配“依附于对象生命周期”的需求:给对象打标签、存私有状态、管理临时关联数据。只要对象存在,数据就在;对象没了,数据自动消失,不用你操心。
真正解决内存泄漏,靠的不是手动调 delete,而是让数据生命周期和主体对象对齐。WeakMap 提供的,正是这种自动对齐的能力。











