当前位置: 首页 > news >正文

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 (强引用) → BigDatakeyA 还被 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 类型 任意 只能是对象
http://www.jsqmd.com/news/689446/

相关文章:

  • VSCode 2026车载开发环境搭建全链路指南:从CANoe集成到AUTOSAR配置的7步落地实操
  • 如何快速搭建企业级网站:Mezzanine CMS 完整指南
  • 从ASAP2标准到你的屏幕:A2L文件生成与校验的完整避坑指南(基于Vector工具链)
  • 题解:洛谷 AT_abc426_b [ABC426B] The Odd One Out
  • Linux 0.11内核时钟中断调试实战:用GDB在Bochs里一步步追踪jiffies变化
  • 从Detect到L0:深入拆解PCIe设备上电链路训练的每一个‘握手’步骤
  • 别再怕数学!用STM32和SimpleFOC库,手把手带你实现无刷电机FOC控制
  • 如何搭建Hermes Agent/OpenClaw?2026年部署及Coding Plan配置详细攻略
  • 别再死记硬背隔离级别了!用MySQL 8.0实战,手把手带你搞懂MVCC的‘快照’到底怎么拍
  • 京家教市场实地调查:北京一对一家教找北师大家教中心 - 教育资讯板
  • 终极指南:如何快速掌握 Viddy 现代监控命令的10个技巧
  • LotusDB批量操作完全指南:大幅提升数据写入效率
  • Blinker Library终极指南:10分钟打造你的首个物联网项目
  • 共享记忆 vs 私有记忆:多 Agent 的记忆架构选择
  • NumPy张量操作指南:从基础到机器学习应用
  • STM32H743用CubeMX配置SPI驱动W5500,从硬件连接到网络配置的保姆级避坑指南
  • **基于Python与BCI接口的脑机交互编程实践:从信号采集到实时控制的全流程实现**在人工智能与神经科学融合加速发展的今天,**
  • APQP实战指南:从概念到量产的结构化质量策划
  • LAN Share:基于Qt C++的零配置局域网文件传输解决方案
  • 3小时重构旧代码库:用C++26反射替代Boost.Hana的4步安全迁移法(附clangd语义补全配置清单)
  • 题解:洛谷 AT_abc426_a [ABC426A] OS Versions
  • 2026年怎么部署Hermes Agent/OpenClaw?搭建及Coding Plan配置保姆级教程
  • YOLOv5至YOLOv12升级:零售柜商品检测软件的设计与实现(完整代码+界面+数据集项目)
  • 终极免费在线EPUB编辑器:5分钟创建专业电子书完全指南
  • YouTube Plus常见问题视频解答:直观解决用户疑惑
  • JS如何通过WebUploader实现理赔视频的跨浏览器分片断点校验与压缩传输插件?
  • 2026年精选:15CrMoG高压合金管一级代理商口碑分析揭晓,15CrMoG高压合金管生产厂家联系电话 - 品牌推荐师
  • 告别卡顿!用uni.request的enableChunked实现小程序流式聊天(附完整代码)
  • 题解:洛谷 AT_abc427_c [ABC427C] Bipartize
  • 新手别怕!用C语言和汇编代码实操理解8088的AX、BX、CX、DX寄存器到底怎么用