map和set是为解决对象键类型限制、数组去重低效、o(n)查找等真实问题而生的工具:map支持任意类型键和o(1)查找,set提供自动去重和o(1)存在判断。

直接上手学 Map 和 Set,别从“概念定义”开始。它们不是新语法糖,而是为解决真实问题而生的工具——比如用对象当字典时键只能是字符串、数组去重要写三行 filter、判断是否存在得遍历整个数组。系统学,就按“问题→工具→用法→避坑→实战”这个链条走。
先搞清楚它们解决什么问题
不用 Map 和 Set,你大概率在这么干:
- 用
{}存数据,结果发现{a: 1, 1: 'x'}的遍历顺序不按你加的顺序;或者想用一个对象做 key,最后全变成"[object Object]"冲突了; - 去重写
[...new Set(arr)]是抄来的,但不知道为什么比arr.filter((v, i) => arr.indexOf(v) === i)快; - 判断某用户是否已登录,每次都要
users.some(u => u.id === targetId),O(n) 查找,页面一卡就是它。
Map 和 Set 就是来终结这些低效写法的:Map 提供任意类型键 + 插入顺序 + O(1) 查找;Set 提供自动去重 + O(1) 判断存在性 + 值唯一性保证。
Map 的核心用法就五条,每天写一遍
别记所有 API,先熟用这五个最常用的方法,覆盖 90% 场景:
-
map.set(key, value)—— 键可以是对象、函数、NaN,甚至另一个 Map; -
map.get(key)—— 找不到返回undefined,不会报错; -
map.has(key)—— 比key in map安全,不查原型链; -
map.delete(key)—— 删除成功返回true,失败返回false; -
map.size—— 直接拿长度,不用Object.keys(map).length。
小技巧:初始化时可传二维数组:new Map([['a', 1], [obj, 'data']]),适合从后端接口批量导入键值对。
Set 的本质是“值的集合”,不是“数组替代品”
很多人把 Set 当成“高级数组”,这是误区。Set 不支持索引、没有 push/pop、不能用下标取值。它只做三件事:
-
set.add(value)—— 重复添加无效,自动忽略; -
set.has(value)—— 判断是否存在,严格相等(NaN和NaN能匹配); -
set.delete(value)—— 删除指定值,不是按索引删。
数组去重别再写 filter:直接 [...new Set(arr)];合并多个数组去重: [...new Set([...a, ...b, ...c])];判断两个数组是否有交集:const s = new Set(a); b.some(x => s.has(x))。
真正拉开差距的是边界认知和排错经验
光会用不够,这几个点踩过才算熟练:
- Map 的键是引用类型时,
map.set({id: 1}, 'a')和map.get({id: 1})拿不到值——因为是两个不同对象,内存地址不同;要用同一个对象变量或用JSON.stringify做标准化键; - Set 里放对象,
new Set([{x:1}, {x:1}])长度是 2,不是 1——对象比较看引用,不是内容; - WeakMap/WeakSet 不可迭代、无
size,只适用于“临时关联+防内存泄漏”场景,比如给 DOM 元素存私有数据; - 遍历 Map 推荐
for (const [key, value] of map),比map.forEach更灵活,能用break和return。
不复杂但容易忽略











