WeakMap 到底"弱"在哪里

用一个实验搞懂 WeakMap 到底"弱"在哪里

Map 持有 key 的强引用,忘了删就永远不释放。WeakMap 持有 key 的弱引用,key 没人用了就自动回收,entry 跟着消失。但"自动回收"到底是怎么自动的?

实验设计

同时往 Map 和 WeakMap 里各塞一条数据:

const strongMap = new Map()
const weakMap = new WeakMap()

let keyA = { label: 'Map-key' }
let keyB = { label: 'WeakMap-key' }

strongMap.set(keyA, new BigData('MapValue'))
weakMap.set(keyB, new BigData('WeakMapValue'))

然后断开外部引用:

keyA = null
keyB = null

问题来了:Map 和 WeakMap 里的数据会怎样?

怎么知道对象被 GC 了

JavaScript 有个 FinalizationRegistry,对象被垃圾回收时会触发回调:

const finalizer = new FinalizationRegistry((label) => {
  console.log(`${label} 被 GC 了`)
})

const key = { name: 'test' }
finalizer.register(key, '这个是 test key')

key = null // 断开引用,等 GC 回收后回调触发

思路很清楚:分别给 Map 的 key 和 WeakMap 的 key 注册 finalizer,看谁的回调触发了、谁的没有。

实验结果

第一步:初始状态

初始状态

第二步:点击添加 entry

往 Map 和 WeakMap 各插入一条数据,两边都注册 FinalizationRegistry。

添加 entry

Map.size = 1,WeakMap 插入成功。

第三步:点击断开 key 引用

把外部变量置 null。Chrome 拍快照时会自动触发一轮 GC,所以这一步同时完成了断开引用和触发 GC。看日志面板:

[Finalizer] WeakMap 的 key "WeakMap-key" 被 GC 了!entry 已自动移除

断开 key 引用后的 Heap Snapshot

只有 WeakMap 的 finalizer 触发了。Map 的没动静。用 Heap Snapshot 搜类名确认一下:

  • MapValue → 还在
  • WeakMapValue → 没了

为什么会这样

keyA = null 之后,各自的引用链长这样:

Map:
  strongMap → keyA (强引用) → BigData
  keyA 还被 Map 拽着,GC 认为它可达,不回收

WeakMap:
  weakMap ➜ keyB (弱引用) → BigData
  弱引用不算"可达",GC 认为 keyB 不可达,回收
  key 被回收,entry 自动消失,value 也跟着被回收

"弱引用"的"弱"就在这:GC 判断对象是否存活时,不把 WeakMap 的引用算进去。对象如果没有其他强引用,就回收,WeakMap 管不住。

顺带解释一个相关问题:为什么 WeakMap 连 .size 都没有?

Map 给你 .size,因为 entry 是确定性的——你不删就不会少。WeakMap 的 entry 随时可能被 GC 回收,给你一个数字没意义,下一秒可能就变了。同理也没有 .keys().values().entries()。你能做的只有 get()set()has()delete(),而且必须拿着 key 对象才能操作。

实际场景

拿 React 举例,你想缓存组件实例对应的数据:

// 用 Map:组件卸载后数据永远不释放
const cache = new Map()
cache.set(componentInstance, hugeData)
// 忘记 cache.delete(componentInstance) → 泄漏

// 用 WeakMap:组件被 GC 后缓存自动消失
const cache = new WeakMap()
cache.set(componentInstance, hugeData)
// 不需要手动清理,组件没了缓存就没了

凡是"缓存的生命周期跟 key 对象一致"的场景,用 WeakMap 就不用操心清理的事。

总结

Map WeakMap
key 引用类型 强引用 弱引用
key 被 GC 不会(Map 拽着) 会(弱引用不算可达)
.size 没有
遍历 可以 不可以
忘记清理 内存泄漏 自动回收
key 类型 任意 只能是对象
posted @ 2026-04-23 22:27  BestElvis  阅读(6)  评论(0)    收藏  举报