Map和Set是ES6原生数据结构,非语法糖;Map支持任意类型键且不强制转字符串,Set基于SameValueZero去重;二者不可直接JSON序列化,需转数组处理。

Map 和 Set 不是语法糖,也不是对象模拟——它们是 ES6 原生的、带内存管理与行为契约的数据结构,用错场景或误用方法会导致隐性 bug。
Map 是键值对集合,且键可以是任意类型
和 Object 最大区别在于:Map 的键不限于字符串或 Symbol,function、{}(对象)、[](数组) 都能当键,且不会被强制转为字符串。
常见错误现象:
用 {} 作为 Object 的键时,实际存的是 "[object Object]";而 Map 能真正区分两个不同对象。
- 新增/更新用
map.set(key, value),不是点号赋值 - 查值必须用
map.get(key),不能用map[key] -
map.keys()返回迭代器,不是数组,要展开需[...map.keys()] - 遍历时顺序按插入顺序,
Object在 ES2015+ 也保证了这一点,但 Map 更可靠
const m = new Map();
const objKey = { id: 1 };
m.set(objKey, 'user A');
console.log(m.get(objKey)); // 'user A' —— 正确匹配
console.log(m.get({ id: 1 })); // undefined —— 新对象,不相等
Set 是去重的值集合,不关心“键”概念
Set 存的是“值”,没有键值映射关系。它内部用 SameValueZero 算法判断重复(即 === 规则,但 NaN 与 NaN 视为相等)。
立即学习“Java免费学习笔记(深入)”;
使用场景:
过滤数组重复项、快速判存(比 arr.includes() 平均快)、实现无序唯一状态集合。
-
set.add(value)返回 Set 本身,可链式调用 -
set.has(value)是 O(1) 查找,比数组includes(O(n))高效得多 -
new Set([1, 2, 2, 3])构造时自动去重,但注意引用类型仍按内存地址判重 - 清空用
set.clear(),不能用set = new Set()替换引用(若其他地方还持有原引用)
const s = new Set();
s.add({}).add({}); // 两个不同对象,都加入
console.log(s.size); // 2
s.add(NaN).add(NaN);
console.log(s.size); // 1 —— NaN 被视为重复
Map 和 Set 都不可 JSON.stringify 直接序列化
直接 JSON.stringify(new Map([[1, 'a']])) 得到 "{}";JSON.stringify(new Set([1,2])) 得到 "{}"。这不是 bug,是设计使然——它们不是普通可枚举对象。
正确做法:
- Map 转数组:用
Array.from(map)或[...map],再 JSON 序列化 - Set 转数组:用
Array.from(set)或[...set] - 若需保留 Map/Set 结构,得自行封装序列化/反序列化逻辑(例如存为
{ type: 'Map', data: [...] })
const m = new Map([['name', 'Alice'], ['age', 30]]); console.log(JSON.stringify([...m])); // [["name","Alice"],["age",30]]
别用 Map/Set 替代简单配置对象或布尔标记
如果只是存几个固定字段如 { theme: 'dark', lang: 'zh' },用 Object 更轻量、可读性更好;如果只记“是否启用某功能”,用布尔变量或 Set 都行,但 Set 多了构造和方法调用开销。
性能影响:
Map/Set 底层有哈希表或类似机制,但初始化成本高于字面量对象;小数据量(
兼容性提醒:
IE 完全不支持,Node.js 0.12+ 开始支持,现代浏览器均已支持。若需兼容旧环境,得用 Babel + polyfill(如 core-js),但注意 polyfill 的 Map/Set 可能不完全等价(比如迭代顺序或错误抛出行为)。
最常被忽略的一点:Map 的 size 是属性,不是方法;Set 同理。写成 map.size() 会报 TypeError: map.size is not a function。











