Map和Set是为解决数组/对象硬伤而生:Map支持任意类型键、保持插入顺序、O(1)查存在;Set专精O(1)存在判断与去重;二者协同可优化嵌套结构与权限查询,但需规避误用场景。

直接从使用场景切入,比死记方法名更有效。Map 和 Set 不是“新语法”,而是为解决数组/对象的硬伤而生——比如去重慢、键只能是字符串、查存在性要遍历。掌握它们的关键,在于理解“什么问题该交给谁处理”。
Map:别再用对象模拟键值对了
Object 的 key 只能是字符串或 Symbol,遇到对象、函数、Date 作 key 就得自己序列化,还容易丢数据(比如 undefined、function)。Map 没这限制:
- 键可以是任意类型:DOM 元素、React 组件实例、new Date()、甚至另一个 Map
- 插入顺序严格保留:for...of 遍历时,顺序和 set() 顺序完全一致
- size 属性直接可用:不用再写 Object.keys(obj).length
- 注意引用相等性:map.set({id:1}, 'a') 和 map.get({id:1}) 永远不匹配,因为两个对象地址不同;要用同一对象引用,或改用 id 字符串作 key
Set:去重只是起点,存在性判断才是高频刚需
很多人只把 Set 当成“数组去重工具”,其实它最不可替代的能力是 O(1) 时间判断某个值是否存在:
- 用 set.has(x) 替代 arr.includes(x):10 万元素下,前者几乎常数时间,后者平均耗时高 80 倍
- NaN 只存一次,null 和 undefined 被视为不同值,对象即使内容相同也各自独立
- 保持原始顺序去重?
[...new Set(arr)]对简单类型够用;但若 arr 含对象且需按首次出现位置保留,就得配合 Map 记录索引,或用filter((v, i) => arr.indexOf(v) === i)
Map + Set 协同:替代嵌套结构的利器
传统用法里,用户权限常写成 { userId: ['admin', 'editor'] },但查权限要先取数组再 includes —— 两步 O(n)。换成 Map
- 查某用户是否有某角色:
rolesMap.get(userId)?.has('admin')—— 一步到位,两次 O(1) - 增删角色无需深拷贝数组,
rolesMap.get(userId).add('publisher')直接生效 - LRU 缓存淘汰最老项?Map 天然有序:
cache.delete(cache.keys().next().value),不用额外维护时间戳或数组
避开典型误用陷阱
不是所有“看起来像 Map/Set”的场景都适合它们:
- 大量只读配置(如 i18n 文案):用 plain object 更轻量,Map 的哈希表开销反而是负担
- 需要稳定排序(非插入序):Set 不提供 sort 方法,也不保证数学意义上的有序;该用数组 + sort
- 频繁新建 Set 在循环里:
arr.map(x => new Set([x, ...others]))会触发大量 GC;应提取共用 Set 或预建布尔查找表 - 交集/并集运算别滥用展开:
new Set([...setA, ...setB])创建中间数组低效;改用迭代器逐个 add 更快


















