JavaScript中的Map和WeakMap都是用来存储键值对的数据结构,但两者在内存管理上的设计差异很大,核心区别体现在对键的引用类型和垃圾回收的触发规则上。

Map的内存回收特性
Map的键可以是任意类型,包括对象、基本类型等,并且Map对键的引用是强引用。这意味着只要Map实例本身没有被垃圾回收,那么作为键的对象也不会被回收,哪怕外部已经没有其他地方引用这个对象。
我们可以通过下面的示例来验证这个特性:
// 创建一个Map实例
const map = new Map();
// 创建一个对象作为键
let obj = { name: "test" };
// 将对象存入Map
map.set(obj, "对应的值");
// 切断外部对obj的引用
obj = null;
// 等待一段时间后检查Map中是否还存在该键
setTimeout(() => {
console.log(map.size); // 输出1,说明对象没有被回收
console.log(map.has({ name: "test" })); // 输出false,因为引用的是不同的对象实例
}, 1000);
从上面的代码可以看到,即使我们把外部的obj变量设为null,Map中存储的键对象依然不会被垃圾回收,因为Map本身还持有这个对象的强引用。
WeakMap的内存回收特性
WeakMap的键只能是对象类型,并且WeakMap对键的引用是弱引用。当作为键的对象没有其他强引用时,即使WeakMap实例还存在,这个对象也会被垃圾回收机制回收,对应的键值对也会从WeakMap中自动移除。
同样用代码示例验证:
// 创建一个WeakMap实例
const weakMap = new WeakMap();
// 创建一个对象作为键
let obj2 = { name: "test2" };
// 将对象存入WeakMap
weakMap.set(obj2, "对应的值");
// 切断外部对obj2的引用
obj2 = null;
// WeakMap没有size属性,无法直接查看大小,我们可以通过间接方式验证
// 这里借助垃圾回收的触发逻辑,实际开发中WeakMap的键被回收后无法主动查询
setTimeout(() => {
// 此时obj2对应的对象已经被回收,WeakMap中对应的键值对也已移除
console.log("WeakMap中的键已被回收");
}, 1000);
需要注意的是,WeakMap没有size属性,也没有clear方法,无法遍历其中的键值对,这是因为WeakMap中的键值对随时可能被垃圾回收,遍历的结果是不稳定的。
两者的核心差异对比
我们可以通过表格更清晰地看到Map和WeakMap在内存回收相关特性上的区别:
| 特性 | Map | WeakMap |
|---|---|---|
| 键的类型 | 任意类型 | 仅对象类型 |
| 键的引用类型 | 强引用 | 弱引用 |
| 键对象无外部引用时的回收情况 | 不回收,只要Map实例存在 | 会被垃圾回收,对应键值对自动移除 |
| 是否可遍历 | 是,支持keys、values、entries等方法 | 否,没有遍历相关方法 |
| 是否有size属性 | 是 | 否 |
适用场景分析
Map适合需要长期存储键值对、需要遍历集合内容、键的类型不限于对象的场景,比如存储用户的配置信息、缓存需要主动管理的数据等。
WeakMap适合需要临时关联对象和数据的场景,比如给DOM元素绑定额外的数据,当DOM元素被移除后,对应的数据可以自动回收,避免内存泄漏。下面是一个给DOM元素绑定数据的示例:
// 创建一个WeakMap存储DOM元素和对应的数据
const domDataMap = new WeakMap();
// 获取一个DOM元素
const div = document.getElementById("testDiv");
// 给DOM元素绑定数据
domDataMap.set(div, { clickCount: 0 });
// 当div元素被从页面移除,且没有其他地方引用时,对应的数据会被自动回收
// 不需要手动去删除WeakMap中的对应项
在实际开发中,我们需要根据是否需要自动回收键对象的内存、是否需要遍历集合等需求,选择合适的集合类型,从而优化应用的内存使用效率。
JavaScriptMapWeakMap内存回收修改时间:2026-07-12 00:30:21