前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库WeakMap 观察者 弱引用订阅
设计设计模式可维护代码

为什么可以用 WeakMap 或 WeakRef 管理观察者订阅来避免内存泄漏?

WeakMap 仅对键弱引用。当主题对象不再被其它强引用时,即使仍作为键也会被回收,对应订阅条目自动失效;但若主题仍存活,仍需显式退订以释放监听器。

前端进阶之旅 · 一题精讲更新于 2026.09.05
设计模式#可维护代码
先看核心答案读代码示例
理解线索

弱引用键的自动清理

  1. 键必须对象只有对象或非注册symbol可作键。原始值不行。
  2. 值可任意值可以是数组、Set等,不限制类型。
  3. 条目随键消失键被回收后,对应的值也会变成垃圾。

只有键是弱引用,值仍由键强引用,但键一消亡值无引用也会被回收。

核心回答

先记住这个答案

观察者模式常用数组保存监听器,若忘记退订会强引用目标,导致内存泄漏。WeakMap 以主题对象为键、订阅集合为值,不阻止主题回收;主题消失后条目自动可回收,监听器若无引用也被回收,从而免除退订操作。WeakRef 可进一步持有弱引用目标,但需配合 FinalizationRegistry 清理。

  • WeakMap 键为弱引用,不阻止对象回收。
  • 主题对象消亡后,其订阅条目可被回收。
  • WeakMap 不可枚举,不能遍历订阅列表。

弱引用如何让订阅自动失效

常规 Map 保存订阅时,键(主题对象)被 Map 强引用。即使主题对象在业务中已不可达,Map 仍使其存活,导致依赖它的监听器集合也一直存活,形成泄漏。WeakMap 则不同:它对键只保持弱引用,不参与垃圾回收可达性判定。主题对象只要没有其它强引用,即便仍在 WeakMap 中,也会被回收。

回收后,该键对应的值(如监听器数组)不再有引用链(键已死),除非值被外部持有,否则也会在下一轮回收。这就让订阅表随主题对象生命周期自动清理。但 WeakMap 不提供键的枚举或大小,因为键的存活状态取决于 GC,不可预测,相关 API 被刻意省略。

页面组件与数据源订阅

观察者模式中常见的泄漏是主题(被观察对象)长生命周期、订阅者短生命周期,主题通过订阅回调间接持有订阅者。WeakMap 的弱引用只能针对键,若直接以长生命周期主题为键存储回调集合,主题仍强引用回调,回调若捕获了短生命周期订阅者则泄漏依旧。WeakMap 更适用于主题自身生命周期较短的场景,例如临时任务或一次性事件源。下面示例模拟这种用法。

代码中 topic 是临时对象,当外部不再引用后,WeakMap 条目变为不可达,其值(回调集合)在没有其他引用时也会被回收,因此无需显式退订。但若主题是长期存活的共享数据源,且回调闭包引用了短生命周期视图,即使视图销毁,主题通过该 WeakMap 的 value 仍持有回调,从而间接持有视图——此时应使用 WeakRef 让主题仅弱持有订阅者,或在视图销毁时显式退订,不能指望键回收。

WeakMap 做主题订阅表JavaScript
const subs = new WeakMap();

function subscribe(topic, callback) {
  if (!subs.has(topic)) subs.set(topic, new Set());
  subs.get(topic).add(callback);
  return () => subs.get(topic)?.delete(callback);
}

function notify(topic, data) {
  subs.get(topic)?.forEach(cb => cb(data));
}

let topic = { name: 'temp' };
subscribe(topic, data => console.log('收到', data));
notify(topic, '第一次'); // 收到 第一次

topic = null; // 不再持有主题
// 下次 GC 后,WeakMap 条目被清除
// 但此处无法观测删除,因为 GC 不确定
查看输出与解释
收到 第一次

该代码模拟短生命周期主题。设置 null 后,weakMap 条目在后续 GC 被回收。不打印删除事件,因不可见。

适用边界与代价

WeakMap 只能以对象作为键,且不能遍历或清空。如果业务需要列出当前所有主题或手动取消所有订阅,WeakMap 无法直接实现。另外,值仍会强引用回调函数,如果回调是长生命周期对象的方法,不解除引用仍会泄漏。WeakMap 只处理主题对象的生命周期,不处理订阅者自身的生命周期。

WeakRef 能直接持有订阅者的弱引用,但使用复杂且需要 FinalizationRegistry 清理参考,否则可能出现回调触发时目标已死的情况。实际工程中,优先使用显式退订,更可控。WeakMap 适合主题对象天然短生命周期或框架层确保主题不再引用时自动回收。

回答前,多想一步

容易答错的地方

以订阅者为键即可防泄漏
错误。若键是订阅者,值中通常含主题引用,主题仍被值强引用。应反转:以被观察的主题为键,回调中对订阅者弱引用。
WeakMap 可枚举或清空
错误。WeakMap 没有 keys()、values()、size 和 clear(),因为键存活依赖 GC,不确定。需要枚举时改用 Map 加手动操作。
试着用自己的话回答

面试官还会怎么问?

WeakRef 相比 WeakMap 有什么不同应用?

WeakRef 允许你持有任意对象的弱引用,可以单独用于订阅者列表。但需在取用时检查是否已被回收,并用 FinalizationRegistry 清理相关注册表数据,代码复杂度高。

主题对象被回收,但监听器仍被用户引用,会发生什么?

WeakMap 条目删除后,监听器集合不再被引用,但监听器本身如果你还保存着变量,仍存活。因为它们被外部引用。泄漏只发生在监听器也无需存活时。

可以用 WeakMap 实现订阅总列表吗?

不能。WeakMap 不可枚举,所以无法统计或遍历所有订阅。若需要列表,应维护一个 Map 并显式清理。

从一道题,走向一组知识

把知识连起来

可维护代码

JavaScript 中观察者模式忘记取消订阅会造成什么内存问题,如何诊断?

深入解释观察者模式忘记退订造成泄漏的诊断和常见内存图。

可维护代码

观察者模式与发布订阅模式在耦合方式上的核心区别是什么?

理解观察者与发布订阅的耦合差异,有助于判断何时可用 WeakMap。

参考资料

  • WeakMap

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 弱引用如何让订阅自动失效
  3. 页面组件与数据源订阅
  4. 适用边界与代价
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑