Map是键值对集合,键支持任意类型;Set是唯一值集合,用于去重和成员判定;二者均用SameValueZero比较,但Map侧重映射查找,Set侧重唯一性判断。

Map 与 Set 的核心区别别搞混
Map 是键值对集合,键可以是任意类型(包括对象、函数、Symbol);Set 是唯一值集合,只存值,不存键。很多人误以为 Set 是“没有键的 Map”,其实两者设计目标完全不同:Map 用于查找映射关系,Set 用于去重和成员判定。
Map 的 key 判定陷阱要小心
Map 使用 严格相等(SameValueZero) 比较键,这意味着 NaN === NaN 为 false,但 Map 中 NaN 仍能被正确识别为同一 key —— 这是它和 Object 的关键差异。但要注意:
- 对象作 key 时,每次 new 都是新引用,不会命中已有 key
- 原始值(如字符串、数字)作 key 没问题,自动包装也安全
- Symbol 作 key 完全独立,互不干扰
Set 的“唯一性”基于 SameValueZero,不是深比较
Set 不会递归比较对象内容,只看引用或原始值是否相同:
-
new Set([{a:1}, {a:1}])长度是 2(两个不同对象) -
new Set([1, '1'])长度是 2(数字 1 和字符串 '1' 不同) -
new Set([0, -0])长度是 1(SameValueZero 认为 0 和 -0 相等) -
new Set([NaN, NaN])长度是 1(NaN 被视为同一个值)
遍历与修改时的常见误操作
Map 和 Set 都支持 for...of、forEach、entries()、keys()、values(),但注意:
- 遍历时直接修改自身(如边 forEach 边
delete或set)不会报错,但行为未定义 —— 可能跳过元素或重复访问,应先收集待操作项再批量处理 - Set 没有类似 Map 的
get()方法,判断存在用has(),取值只能转数组或迭代器后手动获取 - Map 的
set()总是返回 Map 实例,可链式调用;Set 的add()同理,但delete()返回布尔值,不可链式
替代方案选型建议
别一上来就用 Map/Set:
- 键固定且是字符串?Object 或普通对象字面量更轻量、可序列化、调试友好
- 只需简单去重数组?
[...new Set(arr)]简洁有效;但大数据量+频繁增删,考虑是否真需动态维护 Set - 需要按插入顺序遍历?Map/Set 天然保证,Object 不保证(ES2015+ 仅对字符串/符号键按插入序,数字键仍按升序)
- 需要 JSON 序列化?Map/Set 无法直接 JSON.stringify,得手动转成数组或对象结构











