javascript中Map与Set是什么_与对象和数组有何不同?

Map是键可为任意类型的有序键值对容器,Set是基于SameValueZero算法的去重值集合;二者均不支持JSON.stringify直接序列化,需转为数组处理,且在高频操作中提供稳定O(1)性能。

Map 是带键值对的有序集合,不是对象的替代品

Map 本质是「键可为任意类型」的键值对容器,而 Object 的键只能是字符串或 Symbol(即使你写 {1: 'a'},实际键也是字符串 "1")。这导致用对象模拟映射时容易出错:obj[{}] === obj[{}] 总是 true(因为两个空对象都转成 "[

object Object]"),但 map.set({}, 'a') !== map.set({}, 'b') 是完全独立的条目。

实操建议:

  • 需要以函数、对象、日期等作键时,必须用 MapObject 无解
  • Map 保持插入顺序,遍历时按添加顺序返回;Object 的属性顺序在 ES2015 后虽也保证,但仅限字符串/Symbol 键,且原型链上的可枚举属性会干扰 for...in
  • 获取大小:map.size 是 O(1),Object.keys(obj).length 是 O(n)

Set 是去重的值集合,不是数组的简化版

Set 存储唯一值,内部用 SameValueZero 算法判断重复(即 NaN === NaN 为真),而数组靠手动 filter((v, i, a) => a.indexOf(v) === i)[...new Set(arr)] 去重——后者才是 Set 的典型用途。

常见错误现象:

  • 误以为 new Set([{a:1}, {a:1}]) 只存一个对象 → 实际存两个,因对象引用不同
  • arr.includes(item) 在大数组里频繁查找 → 时间复杂度 O(n),换成 set.has(item) 是 O(1)
  • Set 不能通过索引访问元素(没有 set[0]),必须用迭代器或转为数组

Map 和 Set 都不支持 JSON.stringify 直接序列化

直接 JSON.stringify(new Map([['a', 1]])) 得到 "{}"JSON.stringify(new Set([1,2])) 也是 "{}"。这是因为它们没有可枚举的自有属性,JSON.stringify 只处理 plain object 的键值对和 array 的索引项。

正确导出方式:

  • Map → 转为二维数组:
    JSON.stringify([...myMap]) // [["a",1],["b",2]]
  • Set → 转为数组:
    JSON.stringify([...mySet]) // [1,2,3]
  • 反向还原需手动:new Map(JSON.parse(str)) 不行,得 new Map(JSON.parse(str)) 改成 new Map(JSON.parse(str)) → 实际应写 new Map(JSON.parse(str))?不对,要解构:
    const arr = JSON.parse(str); new Map(arr)

性能与内存:Map/Set 比 Object/Array 更适合高频增删查场景

当数据量超过千级、且操作密集(如实时过滤、状态缓存、事件监听器注册表),Mapset/get/deleteSetadd/has/delete 平均时间复杂度都是 O(1),而 Objectin 操作受原型链影响,ArrayincludesindexOffilter 全是 O(n)。

但要注意:

  • 小数据量(Object 和 Array 有深度优化,Map/Set 反而略慢
  • MapSet 实例本身不可 JSON 序列化,若需持久化或跨线程传递(如 postMessage),必须显式转换
  • 调试时 Chrome 控制台显示 Map(2) {"a" => 1, "b" => 2},比 Object 层层展开更直观,但无法像对象那样点开看原型
Map 和 Set 的核心价值不在“语法新”,而在语义明确和底层实现保障——当你需要「键非字符串」「值唯一性」「稳定 O(1) 查找」中的任一条件,它们就不是可选项,而是必选项。