一文搞懂 Map、Set、WeakMap、WeakSet
面试常问,但很多人只会用 Map 和 Set,Weak 这两个一问就懵。今天一次讲清。
一、先看一句话区别
- Map:键值对集合,键可以是任意类型
- Set:值的集合,值不重复
- WeakMap:键只能是对象,弱引用,不可遍历
- WeakSet:值只能是对象,弱引用,不可遍历
二、Map vs 普通对象
普通对象:
js
ini
const obj = {};
obj['name'] = '张三'; // 键只能是字符串/Symbol
Map:
js
c
const map = new Map();
map.set('name', '张三');
map.set(123, '数字键');
map.set({}, '对象键'); // 键可以是任意类型
Map 的优势
- 键可以是任意类型
- 有
.size - 可遍历
- 频繁增删性能好
常用方法
js
arduino
const map = new Map();
map.set('a', 1); // 增
map.get('a'); // 查 → 1
map.has('a'); // 判断 → true
map.delete('a'); // 删
map.size; // 大小
map.clear(); // 清空
for (const [key, value] of map) { ... }
三、Set:不重复的集合
js
vbscript
const set = new Set([1, 2, 3, 3, 3]);
console.log(set); // Set { 1, 2, 3 }
常用方法
js
arduino
set.add(1); // 增
set.has(1); // 判断
set.delete(1); // 删
set.size; // 大小
经典用途:数组去重
js
ini
const arr = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(arr)]; // [1, 2, 3]
四、WeakMap:弱引用的键值对
js
vbnet
const wm = new WeakMap();
const key = {};
wm.set(key, '一些数据');
wm.get(key); // '一些数据'
限制
- 键只能是对象
- 不可遍历 (没有
keys、forEach) - 没有
.size - 键是弱引用
弱引用是什么意思?
js
ini
let key = { name: '张三' };
const wm = new WeakMap();
wm.set(key, '数据');
key = null; // 断开引用
// 因为 wm 是弱引用,这个对象可以被垃圾回收
对比普通 Map:
js
ini
let key = { name: '张三' };
const map = new Map();
map.set(key, '数据');
key = null;
// map 里还留着这个对象,不会被回收 → 内存泄漏
WeakMap 的意义:对象没别的地方用了,自动回收。
五、WeakSet:弱引用的集合
js
ini
const ws = new WeakSet();
const obj = {};
ws.add(obj);
ws.has(obj); // true
ws.delete(obj);
ws.size; // undefined
ws.forEach(...) // ❌ 没有
和 WeakMap 一样,只是它是值的集合,不是键值对。
六、核心对比表
| 特性 | Map | Set | WeakMap | WeakSet |
|---|---|---|---|---|
| 存什么 | 键值对 | 值 | 键值对 | 值 |
| 键/值类型 | 任意 | 任意 | 只能是对象 | 只能是对象 |
| 可遍历 | ✅ | ✅ | ❌ | ❌ |
| 有 size | ✅ | ✅ | ❌ | ❌ |
| 引用方式 | 强引用 | 强引用 | 弱引用 | 弱引用 |
| GC 自动回收 | ❌ | ❌ | ✅ | ✅ |
七、WeakMap 的实际用途
1. 给对象存私有数据
js
kotlin
const privateData = new WeakMap();
class Person {
constructor(name) {
privateData.set(this, { name });
}
getName() {
return privateData.get(this).name;
}
}
p 被回收时,privateData 里的数据也自动消失。
2. DOM 元素关联数据
js
javascript
const cache = new WeakMap();
function attachData(element, data) {
cache.set(element, data);
}
// element 被移除后,关联数据自动回收
用 Map 的话,DOM 元素删了,数据还留着 → 内存泄漏。
3. 缓存
js
csharp
const cache = new WeakMap();
function process(obj) {
if (cache.has(obj)) return cache.get(obj);
const result = /* 复杂计算 */;
cache.set(obj, result);
return result;
}
对象不用了,缓存自动清掉。
八、为什么 WeakMap 不可遍历?
因为弱引用的对象随时可能被 GC 回收。
如果允许遍历,结果会不稳定------你遍历时,对象可能已经被回收了。
所以规范直接禁止遍历。
九、怎么选?
| 需求 | 用哪个 |
|---|---|
| 需要键值对,键任意类型 | Map |
| 去重、快速判断存在 | Set |
| 给对象关联数据,不想阻止回收 | WeakMap |
| 给对象打标记,不想阻止回收 | WeakSet |
十、一句话总结
- Map / Set:可遍历、有大小、强引用
- WeakMap / WeakSet:不可遍历、无大小、弱引用
Weak 的核心就是"不阻止垃圾回收",专门用来给对象挂数据、做缓存、打标记。
附:记忆口诀
Map 存键值,Set 存值。
Weak 只能存对象,不阻止 GC,还不能遍历。
如果这篇文章帮你理清了这四个的区别,点个赞让更多人看到。
有疑问欢迎评论区交流,一起进步。