JavaScript 的 Map、Object 和 WeakMap 有什么区别?分别适合存什么?
下面是一段教学用的模拟面试。
🧑💻 面试官:Object 和 Map 都能保存键值对,为什么还需要 Map?
🙋♂️ 我:Map 支持对象作为键,遍历和统计数量也比较方便。
🧑💻 面试官:用两个内容相同的对象,能从 Map 里查出同一个值吗?
🙋♂️ 我:不能,它们是两个不同的对象。
🧑💻 面试官:假设咱们给每个 DOM 元素保存额外信息,元素被移除以后,这份 Map 会不会还把它留住?换成 WeakMap 又能不能遍历全部键?
先问键代表什么,再问这份关联要不要延长对象的存活时间。
面试速答(60 秒版)
Object 更适合表达一个有字段的记录,例如用户资料;它的属性键主要是字符串和 Symbol。Map 更适合动态的键值集合,键可以是任意值,提供 size 和明确的遍历接口。
如果 Map 的键是对象,比较的是对象身份,不是字段内容。只要 Map 仍然保留这些条目,它对键和值的强引用就可能让相关对象继续存活。
WeakMap 适合把额外信息关联到可被回收的对象上,这份关联本身不会阻止键被回收。但它不提供键遍历和 size,不能拿来做需要列出全部成员的普通字典。当前 JavaScript 也允许非注册 Symbol 作为 WeakMap 的键,不能简单背成“所有 Symbol 都不能用”。

知识点详解:给页面元素保存信息,容器应该怎么选?
Object 保存字段,Map 保存一组映射
假设咱们拿到一个用户资料,有 name、age、avatar 三个固定字段。用对象表达很自然:代码通过字段名读取,接口也容易序列化成 JSON。
再换一个需求:页面上的每个按钮,都要关联一份统计信息。按钮元素本身就是一个对象,咱们希望用它直接查出对应记录。
这时 Map 很合适。键可以是按钮对象,查找时直接拿同一个按钮对象调用 get,不必先给每个元素造一个字符串编号。
Object 的普通属性键则会按字符串或 Symbol 规则处理。把两个普通对象直接当作属性键,可能都变成相同的字符串,覆盖彼此。这个差别不是“Map 更高级”,而是两种容器表达的数据关系不同。MDN Map给出了与 Object 的比较。
对象键看身份,不会帮你比较字段
下面是 TypeScript 示例:
const first = { id: 7 };
const second = { id: 7 };
const data = new Map<object, string>();
data.set(first, "第一份记录");
console.log(data.get(first)); // 第一份记录
console.log(data.get(second)); // undefined
first 和 second 的内容一样,但它们是两次创建出来的对象。Map 不会逐个读取字段,猜测它们代表同一个用户。
如果真正的业务身份是用户 ID,就把稳定的 ID 作为键;如果身份是页面上的某个具体元素,就用元素对象。选择键之前,先说清楚“同一个”到底是同一份对象,还是同一个业务编号。
数字键还有明确的比较规则,例如 Map 能稳定地把 NaN 作为同一个键处理,+0 和 -0 则被视为同一个键。不能简单把所有情况概括成严格相等运算符。
本题讨论的是 JavaScript 集合的具体语义,Python dict 和弱引用容器并没有逐项相同的键规则,因此不把示例机械翻译成所谓同款 API。

从 DOM 移除,不代表所有地方都不再引用
假设一个按钮已经从页面移除了,但全局 Map 仍然拿这个元素当键,保存统计信息。
这个元素虽然看不见了,却仍然可以从 Map 找到。只要相关强引用还在,它就可能继续存活,垃圾回收器不能只因为元素离开页面就把它清掉。
使用 Map 时,可以在元素结束使用后明确 delete;如果整份映射都结束了,也可以清空或释放容器。这里需要先确认数据是否还要被查询,不能为了省内存随手删掉仍有用的记录。
如果咱们只希望“元素还存在的时候能查到附加信息”,不需要让这份关联延长元素的生命周期,就可以考虑 WeakMap。
WeakMap 放弃遍历,换来了什么?
WeakMap 让键的存活取决于外部是否仍然需要它,而不是因为这份映射就把它留下。
因此,它可以用于保存某个对象的私有附加数据、缓存与对象关联的结果等。外部仍拿着对象时,就可以用同一个键查找;外部不再需要这个键时,映射本身不阻止回收。
那么,为什么不能返回所有键,也没有 size?
因为垃圾回收的具体时机会变化。若允许观察完整键集合,就容易让程序逻辑依赖回收器此刻清理了谁。WeakMap 的 API 选择不提供这种可观察的枚举结果。MDN WeakMap说明了这个限制。
也不要承诺把变量设为 null 后,下一毫秒 WeakMap 的条目一定消失。开发者通常不能把垃圾回收的精确时间作为业务控制机制。
弱关联,不等于程序再也不会泄漏
假设事件监听器、数组或其他全局缓存还拿着这个元素,它仍然有外部引用。换成 WeakMap,并不能把那些引用一起删除。
同样,如果咱们把查出来的附加对象放进别的强引用容器,而它又保存了对元素的引用,也要继续检查整条引用关系。
不过,仅仅是 WeakMap 内部的值引用回自己的键,不能直接照搬普通强引用环的直觉,断言键因此永远不会回收;弱集合的回收语义需要结合整体可达关系判断。MDN 内存管理对此有进一步说明。
验证 Map 的键身份和遍历行为很直接,可以写断言。检查泄漏则需要看实际引用路径与内存快照;不要用一个固定延时没看到回收,就宣布 WeakMap 失效。

面试官继续追问
WeakMap 只能用对象当键吗?
常见用法是对象键。当前规范也支持非注册 Symbol,也就是不能用 Symbol.for 注册的那类 Symbol。需要兼容旧运行环境时,再检查目标环境支持,别把历史规则当永久规则。
Map 可以直接 JSON.stringify 吗?
不能期待它自动变成想要的键值 JSON。需要定义转换方式,尤其对象键在跨进程传输以后不再有原来的对象身份。接口设计通常更适合明确的字符串 ID 与记录结构。
不需要原型字段的字典,还能使用 Object 吗?
可以考虑无原型对象,并用明确的自有属性检查。但这不会让它获得任意对象键的身份语义,仍要按实际键类型和遍历需求选择。
面试速记卡
- Object:适合表达字段记录,属性键按字符串与 Symbol 规则处理。
- Map:动态键值集合,支持任意值键、size 与遍历。
- 对象键:比较身份,相同字段不代表同一个键。
- WeakMap:弱关联键,不提供全部键枚举和 size。
- 内存判断:检查完整引用路径,不把弱关联当定时清理机制。
公司面试真题
这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。
浏览公司面试真题 →