Map和Set是为高频查找、去重、映射设计的原生结构,非对象/数组替代品;Map键支持任意类型且保序,Set去重时间复杂度O(1),优于数组O(n²)。

Map 和 Set 是专门做查找的键值/唯一值容器
它们不是对象或数组的替代品,而是为高频查找、去重、映射关系设计的原生数据结构。用错场景(比如只存几个静态配置就硬套 Map)反而增加开销;该用却不用(比如频繁 includes 数组查重),性能会掉得明显。
为什么不能直接用对象代替 Map?
对象的 key 只能是字符串或 Symbol,而 Map 的 key 可以是任意类型——函数、对象、null、甚至另一个 Map。更关键的是:对象不保证插入顺序(尤其在旧引擎或非字面量创建时),而 Map 遍历一定按插入顺序返回。
- 错误写法:
const obj = {}; obj[{}] = 'a';→ 实际 key 被转成"[object Object]",多个空对象都撞成同一个 key - 正确写法:
const map = new Map(); map.set({}, 'a');→ 每个对象都是独立 key - 额外代价:对象要手动维护
size,Map直接有map.size
Set 去重比数组 filter + indexOf 快得多
Set 底层是哈希实现,add/has 平均时间复杂度是 O(1);而数组去重常用 arr.filter((v, i) => arr.indexOf(v) === i),每次 indexOf 都要遍历,整体是 O(n²)。
- 简单去重:
[...new Set(arr)]—— 清晰、快、一行解决 - 但注意:
Set只对原始值(string/number/boolean)和同一引用对象判等;[1,2] === [1,2]是 false,所以两个相同数组放进Set仍是两个元素 - 如果要深去重数组或对象,
Set不适用,得自己序列化或用库
别把 Map/Set 当万能筐,小心内存和可读性陷阱
它们没有数组的索引访问(map[0] 无效),也不支持 map.forEach 以外的高阶方法(如 map.map、filter)。想链式处理,得先转成数组(Array.from(map.values())),这会触发一次遍历+内存分配。
- 误用场景:只存 3 个配置项,却用
new Map([['host', 'a'], ['port', 80], ['ssl', true]])—— 对象更轻、可读性更好 - 真需要动态增删 + 快速查 key 时才上
Map;只要去重或存在性判断,优先Set - Node.js 早期版本(Map 的序列化(JSON.stringify)会丢失数据,必须手动展开;现在虽已支持,但跨环境传输仍建议转 plain object
最常被忽略的一点:Map 和 Set 的迭代器不可复用。调用一次 map.keys() 得到的迭代器,遍历完就耗尽了,再 for...of 就什么也不吐——不是 bug,是设计如此。需要多次遍历时,得重新调用方法或缓存为数组。









