Map 和 Set 有什么特点?和 Object、Array 有什么区别?
简化版
Map 是键值对集合,键可以是任意类型;Set 是不重复值集合。相比 Object,Map 更适合频繁增删查和任意键;相比 Array,Set 更适合去重和快速判断存在。
详细版
Map 示例:
const map = new Map();
map.set({ id: 1 }, 'user');
map.get(key);
map.has(key);
Set 示例:
const set = new Set([1, 2, 2]);
console.log([...set]); // [1, 2]
Object 的 key 主要是字符串或 Symbol,Map 的 key 可以是对象、函数、NaN 等。Set 判断重复使用 SameValueZero,NaN 会被认为等于 NaN。
完整版教学
一、Map 解决 Object 做字典的痛点
用 Object 做字典时,key 会转成字符串:
const obj = {};
obj[{ a: 1 }] = 'x';
对象 key 会变成 "[object Object]",容易冲突。Map 可以直接用对象作为 key,引用不同就是不同 key。
二、Map 的遍历顺序
Map 会按插入顺序遍历:
for (const [key, value] of map) {
console.log(key, value);
}
它还有明确的 size 属性,比 Object 统计 key 数量更方便。
三、Set 的典型用途
数组去重:
const unique = [...new Set(list)];
快速存在性判断:
const selected = new Set(ids);
selected.has(id);
当数据量较大时,Set.prototype.has 通常比数组 includes 更适合高频查找。
四、WeakMap 和 WeakSet 简要区别
现代 ECMAScript 中,WeakMap 的 key 可以是对象或非全局注册的 Symbol,且是弱持有,不会仅因作为键就阻止垃圾回收。工程中最常见的键仍是对象,适合存对象私有元数据、DOM 关联数据。它不可遍历,也没有 size,代码不能观察键何时被回收。
五、面试追问与工程落地
Map/Set 常被追问“对象作为 key 时怎么比较”。Map 使用引用身份比较对象 key。两个长得一样的对象字面量不是同一个引用,所以会被当成不同 key。这个点在缓存、节点映射、权限集合里很重要。
还可能问 Set 去重对象是否有效。new Set([{id:1}, {id:1}]) 不会按 id 去重,因为两个对象引用不同。对象数组去重要用 Map 按业务 id 建索引,或者手动维护 key。
工程里 Map 不一定总比 Object 好。普通 JSON 数据、接口参数、简单记录结构用 Object 更直观;需要任意类型 key、频繁增删、保持插入顺序、明确 size 时再优先 Map。
六、从相等规则与生命周期选择集合
Map 和 Set 判断键或成员时使用 SameValueZero 语义。它与 === 大体一致,但 NaN 会被视为等于自身,+0 与 -0 也视为同一个值;对象仍按引用身份比较,内容相同的两个对象是两个键。
const set = new Set([NaN, NaN, +0, -0])
console.log(set.size) // 2
const map = new Map()
map.set({ id: 1 }, 'A')
console.log(map.get({ id: 1 })) // undefined,不是同一引用
| 需求 | 合适结构 | 核心原因 |
|---|---|---|
| 任意类型键、需要遍历 | Map | 键类型不受字符串限制 |
| 唯一值集合 | Set | 内建去重和成员判断 |
| 给对象附加元数据且不阻止回收 | WeakMap | 键是弱持有且不可枚举 |
| JSON 传输的普通记录 | Object | 序列化和字段语义直观 |
规范只要求这些集合在平均情况下提供次线性访问时间,并没有承诺每次操作都严格是 O(1)。现代 ECMAScript 的弱集合可弱持有对象或非全局注册的 Symbol;工程兼容性答题中最常见、最稳妥的 WeakMap 键仍是对象。
WeakMap 不能枚举、没有
size,正是为了避免通过 API 观察垃圾回收时机;不能把它当作“会自动清空的 Map”来调试业务数据。
七、常见误区与追问
- 误区:Map 的键最终都会转成字符串。 Map 保留键的类型与身份,数字
1和字符串"1"是两个键。 - 误区:Set 能按对象内容自动去重。 对象按引用比较,两个结构相同但引用不同的对象会同时保留。
- 误区:WeakMap 的键回收时间可以预测。 垃圾回收时机不可观察也不受业务代码保证。
- 追问:Set 为什么能去掉多个 NaN? SameValueZero 把
NaN与NaN判断为相同成员。 - 追问:Map 一定比 Object 快吗? 不一定,性能取决于数据规模、操作模式和引擎;应先按键语义、遍历和序列化需求选择。
- 追问:WeakMap 为什么不能遍历? 若能枚举键,程序就可能观察某个对象何时被回收,带来不确定行为。
- 追问:Map 如何转换为可 JSON 序列化的数据? 可按协议转成条目数组
Array.from(map);键若不是 JSON 可表达类型,还需自行编码。
八、加强记忆
Map 记“任意 key 的字典”,Set 记“不重复值的集合”。Object 更像普通记录结构,Array 更像有序列表;需要频繁查存在和去重时优先想 Set。
选择集合前先问键的类型、是否要枚举以及是否应影响键的生命周期,比只背 API 更可靠。