javascript中的Map和Set对象有何用途_与普通对象和数组的对比分析【教程】

Map和Set用于高效处理键值映射与唯一值集合:Map支持任意类型键、插入顺序迭代;Set基于SameValueZero去重,适用于存在性检查与去重场景。

Map 和 Set 不是用来替代对象或数组的,而是解决它们在特定场景下无法高效、准确表达“键值映射”和“唯一值集合”的问题。

Map 适合存“任意类型键 + 顺序敏感”的键值对

普通 Object 的键只能是字符串或 Symbol,且遍历时顺序不保证(实际引擎虽大多按插入顺序,但规范未规定);而 Map 允许任意类型作键(如对象、函数、NaN),且严格按插入顺序迭代。

常见错误现象:obj[{a:1}] = 'x' 实际变成 obj['[object Object]'] = 'x',键被强制转为字符串,多个不同对象会冲突。

实操建议:

  • Map 存以 DOM 元素、React 组件实例、类实例为键的元数据
  • 需要频繁增删键值对时,Mapdelete()delete obj[key] 更可靠(后者不触发原型链更新,且无法删除 undefined 值)
  • 遍历时优先用 map.forEach()for (const [key, val] of map),而非 Object.keys() + for...in
const m = new Map();
const keyObj = { id: 1 };
m.set(keyObj, 'data for obj');
m.set(NaN, 'nan key'); // ✅ NaN 可作键,且能正确 get
console.log(m.get(keyObj)); // 'data for obj'
console.log(m.get(NaN));    // 'nan key'

Set 专用于去重 + 快速存在性检查

Array 去重要写 [...new Set(arr)] 或用 filter() + indexOf(),低效且不能处理引用类型;Set 内部用哈希实现,add()/has() 平均时间复杂度 O(1),且自动去重。

使用场景:

  • 过滤重复请求参数(如多个 fetch 请求共用同一组 ID,需去重后批量查)
  • 记录已处理的

    事件源(如防止同一按钮连点多次触发)
  • 代替布尔标记对象是否“已访问”,避免在对象上挂临时属性污染结构

注意:Set 的去重基于 SameValueZero 算法(类似 ===,但 NaN 与自身相等),所以 new Set([NaN, NaN]) 长度为 1,但 {}{} 仍是两个不同值,不会去重。

const seen = new Set();
const btn = document.getElementById('submit');
btn.addEventListener('click', () => {
  if (seen.has(btn)) return;
  seen.add(btn);
  // 执行提交逻辑
});

性能与内存开销:别为了“时髦”滥用

MapSet 比原生对象/数组略重——它们是集合类结构,有额外的内部哈希表和迭代器支持。如果只是静态配置项(如 { theme: 'dark', lang: 'zh' }),用 Object 更轻量、可压缩、V8 优化更好。

容易踩的坑:

  • Map 当成“高级对象”到处用,结果调试时发现 console.log(map) 不显示内容,得用 map.entries() 或展开符 [...map]
  • 误以为 Set 能深比较对象,写 set.add({x:1}); set.has({x:1}) 返回 false(因为是不同引用)
  • Map.prototype.keys() 得到的是迭代器,不是数组,不能直接调用 .map(),要先转 Array.from() 或展开

与 JSON 序列化/反序列化的天然隔阂

JSON.stringify() 完全忽略 MapSet,直接返回空对象 {} 或空数组 []。这意味着它们不适合直接作为跨环境数据载体(如 localStorage、fetch body、WebSocket 消息)。

若必须传输,需手动转换:

  • Map → 数组:用 Array.from(map.entries()),反向用 new Map(arr)
  • Set → 数组:用 [...set],反向用 new Set(arr)
  • 深层嵌套时,得递归处理,或用自定义 toJSON 方法(但 JSON.stringify 不会自动调用它)

这其实是设计使然:Map/Set 是运行时行为结构,不是数据格式。混淆这两者,是很多“为什么存不进 localStorage”的根源。