JavaScript提供了四种常用的集合类数据结构,分别是Map、Set、WeakMap和WeakSet,很多开发者容易混淆它们的使用场景,实际上WeakMap、WeakSet和普通Map、Set在核心设计上有本质区别。

键值类型约束差异
普通Map和Set对存储的键和成员类型没有限制,可以是任意原始类型或者对象类型:
// 普通Map可以存储任意类型的键
const normalMap = new Map();
normalMap.set(1, '数字键');
normalMap.set('str', '字符串键');
normalMap.set(true, '布尔键');
normalMap.set({ a: 1 }, '对象键');
// 普通Set可以存储任意类型的成员
const normalSet = new Set();
normalSet.add(1);
normalSet.add('str');
normalSet.add(true);
normalSet.add({ a: 1 });
而WeakMap的键只能是对象类型,不能是原始类型;WeakSet的成员也只能是对象类型,存储原始类型会直接抛出错误:
// WeakMap键只能是对象
const weakMap = new WeakMap();
weakMap.set({ a: 1 }, '合法');
// weakMap.set(1, '不合法'); // 会抛出TypeError
// WeakSet成员只能是对象
const weakSet = new WeakSet();
weakSet.add({ a: 1 });
// weakSet.add(1); // 会抛出TypeError
引用类型与垃圾回收差异
这是两者最本质的区别。普通Map和Set会对其存储的键、值、成员持有强引用,只要集合本身没有被垃圾回收,其中的内容就不会被回收,哪怕外部已经没有其他引用指向这些内容:
let obj = { name: 'test' };
const normalMap = new Map();
normalMap.set(obj, 'value');
obj = null; // 外部引用置空
// 此时normalMap仍然持有obj的强引用,obj不会被垃圾回收
console.log(normalMap.has({ name: 'test' })); // 此处返回false是因为比较的是引用,不是值相等
而WeakMap和WeakSet对其键、成员仅持有弱引用,当键或成员对象外部没有其他强引用时,即使还在WeakMap或WeakSet中,也会被垃圾回收,且WeakMap/WeakSet会自动移除对应的条目:
let obj = { name: 'test' };
const weakMap = new WeakMap();
weakMap.set(obj, 'value');
obj = null; // 外部引用置空后,obj没有强引用,会被垃圾回收,weakMap中对应条目自动移除
// 由于无法遍历weakMap,无法验证,但该机制是Weak系列结构的核心特性
可遍历性与方法差异
普通Map和Set支持完整的遍历操作,拥有keys()、values()、entries()、forEach()方法,也可以使用for...of遍历,还有size属性可以获取集合长度:
const normalSet = new Set([1, 2, 3]);
console.log(normalSet.size); // 3
for (const item of normalSet) {
console.log(item); // 依次输出1、2、3
}
WeakMap和WeakSet没有遍历相关的方法和属性,也没有size属性,这是因为弱引用的对象随时可能被回收,遍历的结果是不稳定的,所以语言层面直接禁止了遍历操作:
| 特性 | Map/Set | WeakMap/WeakSet |
|---|---|---|
| 键值/成员类型 | 任意类型 | 仅对象类型 |
| 引用类型 | 强引用 | 弱引用 |
| 可遍历性 | 支持 | 不支持 |
| size属性 | 有 | 无 |
| 清空方法 | 有clear()方法 | 无clear()方法 |
适用场景差异
普通Map和Set适合需要存储任意类型数据、需要遍历集合、需要知道集合大小的场景,比如存储键值对配置、去重数组等。
WeakMap和WeakSet适合需要临时关联对象数据、不需要遍历、希望避免内存泄漏的场景,比如给DOM元素附加私有数据,当DOM元素被移除时,附加的数据会自动被回收:
// 给DOM元素附加私有数据,避免内存泄漏
const elementData = new WeakMap();
const dom = document.getElementById('test');
elementData.set(dom, { clickCount: 0 });
// 当dom元素从页面移除,没有其他引用时,elementData中对应的条目会自动被回收
注意WeakMap和WeakSet的弱引用仅针对键和成员,值部分仍然是强引用,如果值引用了键对象,还是会形成强引用链,影响垃圾回收。
WeakMapWeakSetMapSetJavaScript修改时间:2026-06-15 07:00:34