Map与Set的高级用法关键在于理解其底层行为差异:Map以引用为键、保持插入顺序、无原型污染,适用于DOM元数据绑定、Memoization及多维映射;Set提供O(1)存在性判断与NaN安全去重,适合白名单控制与集合运算;需避开引用相等误判、浅层去重及WeakMap/WeakSet使用边界等陷阱。

掌握 Map 与 Set 的高级编程模式,关键不在背方法,而在理解它们的底层行为差异和适用边界。学得快的方法是:先厘清“什么时候非用不可”,再通过典型场景反推用法,最后避开高频陷阱。
Map 的高级用法:别只当字典用
Map 真正的优势不是“能存对象作 key”,而是“键的唯一性由引用决定 + 插入顺序保留 + 无原型干扰”。这催生了几种实用模式:
- DOM 元素元数据绑定:把按钮、输入框等 DOM 节点直接作为 key,存它的校验状态、上次提交时间等——不污染元素属性,也不怕 key 冲突;
- 缓存函数计算结果(Memoization):用参数数组 JSON.stringify 后作 key(注意函数/undefined/Symbol 会丢失),或对简单参数组合用 new Map([[[a, b], result]]);
-
多维映射建模:比如
new Map().set(user.id, new Map().set('permissions', [...]).set('theme', 'dark')),比嵌套普通对象更安全、可迭代、易清理; -
避免隐式类型转换坑:
map.set('1', 'str')和map.set(1, 'num')是两个独立项,而obj['1']和obj[1]会指向同一个属性。
Set 的高级用法:去重只是起点
Set 的核心价值是 O(1) 的 has() 查找和天然去重语义,进阶用法围绕“快速判断存在性”和“集合运算”展开:
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
-
高效白名单/黑名单控制:把允许操作的 action 名称存进 Set,每次执行前
if (allowedActions.has(action)) {...},比数组includes()快得多; -
差集与交集手动实现:没有原生
difference(),但可以简洁写出:const diff = new Set([...setA].filter(x => !setB.has(x))));const intersection = new Set([...setA].filter(x => setB.has(x)))); -
NaN 安全去重:
new Set([NaN, NaN, 1])只留一个 NaN,而数组filter((v, i, a) => a.indexOf(v) === i)会保留两个(因NaN !== NaN); -
对象数组浅去重:若需按某个字段(如
id)去重,可配合 Map:const uniqueById = [...new Map(arr.map(item => [item.id, item])).values()];
必须避开的三个认知误区
这些点看似细节,却是调试时最耗时间的地方:
-
Map 的 key 不是“内容相等”,而是“引用相等”:两个
{id: 1}对象即使一模一样,也是不同 key;真要按内容判等,得自己序列化或用 WeakMap(仅限对象且不阻止 GC); -
Set 去重不递归:
new Set([{a:1}, {a:1}])长度是 2,它只做 SameValueZero 比较,不深比较对象属性; -
别混淆
Array.from(new Set(arr))和[...new Set(arr)]:两者效果完全一样,后者更简洁;真正要注意的是,它们都只能处理一层扁平结构,嵌套数据仍需手动逻辑。
WeakMap 与 WeakSet:专治内存泄漏
当你需要给第三方对象(如库返回的实例、DOM 元素)附加私有数据,又不想影响其生命周期时,WeakMap / WeakSet 是唯一选择:
- WeakMap 的 key 必须是对象,且是弱引用——只要该对象在别处无其他引用,GC 就能回收它,连带 WeakMap 中的条目也会自动消失;
- WeakSet 只能存对象,不能遍历,也没有
size;适合标记“已处理过”这类一次性状态; - 它们都不能用原始值作 key/value,也不支持 clear() 以外的批量操作,这是为内存安全做的取舍。


















