const map = new Map();
let obj = { name: "A" };
map.set(obj, "value");
obj = null;
你以为:
“我已经 obj = null
啦,应该没人引用 { name: "A" }
啊,为什么还不能回收呢?”
🧠 真相:Map 自身会“记住”你的对象!
当你执行:
map.set(obj, "value");
发生了什么?
map
存储了一个 内部引用(强引用) 指向{ name: "A" }
。- 即使你外部
obj = null
,map
里面仍然引用着它。 - 所以在 JS 引擎看来,这个对象还“活着” —— 它依然是“可达”的。
🧬 “可达性” 是垃圾回收的核心依据
JavaScript 的垃圾回收机制不看你“写没写 null”,它只看对象是否还有引用指向它:
- 如果有任何活着的地方引用它(强引用) ,那它就不会被 GC 回收。
- Map 是一个活着的对象,它自己持有 obj 的引用。
所以:
map.set(obj, "value"); // obj 在 Map 中“活着”
obj = null; // 外部不再引用,但 Map 还在
// --> obj 对应的对象仍然不能回收
对比:Map vs WeakMap
操作 | Map | WeakMap |
---|---|---|
set(obj, val) | 创建一个强引用 → obj 永远活着 | 创建一个弱引用 → obj 可被 GC |
obj = null | Map 仍保留对象 → 不可回收 | 没有引用了 → GC 会自动清掉条目 |
举个更直观的例子:
let obj = { name: "A" };
const map = new Map();
map.set(obj, "value");
obj = null; // 你以为断开了,但实际上...
Map 里相当于内部还保留着这个:
map = {
[[object Object]]: "value"
}
→ { name: "A" }
仍然“藏”在 map 的键里,无法被回收。
✅ 总结
Map
是普通对象容器,所有键都是强引用,会阻止 GC。- 即使你外部
obj = null
,Map 本身还引用着那个对象,它就还活着。 - 如果你想让这个引用是“可丢弃”的(可被 GC 回收),就应该用
WeakMap
。