Map、Set、WeakMap、WeakSet 的区别
ES6 四种集合类型的特性对比与使用场景
问题
Map、Set、WeakMap、WeakSet 有什么区别?分别在什么场景下使用?
解答
Set - 值的集合
存储唯一值,自动去重。
const set = new Set();
// 添加值
set.add(1);
set.add(2);
set.add(2); // 重复值会被忽略
console.log(set.size); // 2
console.log(set.has(1)); // true
// 遍历
for (const value of set) {
console.log(value); // 1, 2
}
// 数组去重
const arr = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(arr)]; // [1, 2, 3]
Map - 键值对集合
键可以是任意类型,不限于字符串。
const map = new Map();
// 任意类型作为键
const objKey = { id: 1 };
const fnKey = () => {};
map.set(objKey, 'object value');
map.set(fnKey, 'function value');
map.set('name', 'string value');
console.log(map.get(objKey)); // 'object value'
console.log(map.size); // 3
// 遍历
for (const [key, value] of map) {
console.log(key, value);
}
// 初始化时传入数组
const map2 = new Map([
['a', 1],
['b', 2]
]);
WeakSet - 弱引用值集合
只能存储对象,不阻止垃圾回收。
const weakSet = new WeakSet();
let obj = { name: 'test' };
weakSet.add(obj);
console.log(weakSet.has(obj)); // true
// obj 被置空后,WeakSet 中的引用会被自动回收
obj = null;
// weakSet 中的对象会在下次 GC 时被清除
// 不能存储原始值
// weakSet.add(1); // TypeError
// 不可遍历,没有 size 属性
// weakSet.forEach() // 不存在
// weakSet.size // undefined
WeakMap - 弱引用键集合
键必须是对象,键的引用不阻止垃圾回收。
const weakMap = new WeakMap();
let element = document.getElementById('app');
// 存储 DOM 元素相关数据
weakMap.set(element, {
clickCount: 0,
bindTime: Date.now()
});
console.log(weakMap.get(element)); // { clickCount: 0, bindTime: ... }
// 当 element 从 DOM 移除且无其他引用时
// WeakMap 中的条目会自动清除,避免内存泄漏
element = null;
// 键必须是对象
// weakMap.set('key', 'value'); // TypeError
对比表格
| 特性 | Set | Map | WeakSet | WeakMap |
|---|---|---|---|---|
| 存储内容 | 值 | 键值对 | 值(仅对象) | 键值对(键仅对象) |
| 键/值类型 | 任意 | 任意 | 对象 | 键为对象 |
| 可遍历 | ✅ | ✅ | ❌ | ❌ |
| 有 size | ✅ | ✅ | ❌ | ❌ |
| 弱引用 | ❌ | ❌ | ✅ | ✅ |
使用场景
// 1. Set:去重、集合运算
const setA = new Set([1, 2, 3]);
const setB = new Set([2, 3, 4]);
// 交集
const intersection = new Set([...setA].filter(x => setB.has(x))); // {2, 3}
// 并集
const union = new Set([...setA, ...setB]); // {1, 2, 3, 4}
// 2. Map:需要非字符串键、保持插入顺序
const userRoles = new Map();
const user1 = { id: 1 };
userRoles.set(user1, ['admin', 'editor']);
// 3. WeakSet:标记对象,不影响 GC
const visited = new WeakSet();
function process(obj) {
if (visited.has(obj)) {
return; // 已处理过
}
visited.add(obj);
// 处理逻辑...
}
// 4. WeakMap:存储对象私有数据、缓存
const privateData = new WeakMap();
class User {
constructor(name, password) {
this.name = name;
// 私有数据存在 WeakMap,外部无法访问
privateData.set(this, { password });
}
checkPassword(pwd) {
return privateData.get(this).password === pwd;
}
}
const user = new User('tom', '123456');
console.log(user.name); // 'tom'
console.log(user.password); // undefined
console.log(user.checkPassword('123456')); // true
关键点
- Set 存唯一值,Map 存键值对,键可以是任意类型
- Weak 版本只能用对象作为键/值,且是弱引用
- 弱引用不阻止垃圾回收,适合存储 DOM 元素或对象的附加数据
- Weak 版本不可遍历、没有 size,因为内容随时可能被回收
- WeakMap 常用于实现私有属性和缓存
目录