Map 是一个键类型不限、保持插入顺序、可遍历且具备原生 size 和迭代器的键值容器;其键支持对象/Symbol/NaN等任意类型,遍历严格按写入顺序,增删性能稳定 O(1),适用于动态映射场景。

Map 是什么:一个带顺序、可遍历、键类型不限的键值容器
Map 是 JavaScript 内置的集合类型,本质是一个键值对(key-value)映射表,但和 {} 普通对象有根本区别:它的键可以是任意类型(函数、对象、Symbol、甚至 null),不强制转为字符串;插入顺序严格保留,遍历时按写入顺序返回;且原生支持 size 属性和迭代器接口。
为什么 Map 的键能放对象,而普通对象不行
普通对象的所有键都会被隐式调用 toString() 转成字符串。比如 { [{}]: 1 } 实际变成 { "[object Object]": 1 };多个不同对象当键会互相覆盖。Map 则用 SameValueZero 算法比较键——new Date() 和 new Date() 是两个不同键,NaN 也能正确作为键(普通对象里 NaN 键会被转成 "NaN")。
const obj1 = { id: 1 };
const obj2 = { id: 2 };
const map = new Map();
map.set(obj1, "user A");
map.set(obj2, "user B");
console.log(map.size); // 2 ✅
console.log(map.get(obj1)); // "user A"
const plain = {};
plain[obj1] = "user A";
plain[obj2] = "user B";
console.log(Object.keys(plain).length); // 1 ❌(都是 "[object Object]")
遍历顺序和性能差异:Map 更适合动态增删场景
普通对象的属性遍历顺序在 ES2015 后虽已规范(先数字键升序,再插入顺序),但仍有兼容性顾虑;Map 则从设计上就保证「插入即顺序」,且 for...of、keys()、values()、entries() 全部原生支持。更重要的是:频繁 delete + set 操作时,Map 的时间复杂度稳定在 O(1),而普通对象在 V8 中可能触发隐藏类重建或字典模式切换,带来不可预期的性能抖动。
-
Map.prototype.has(key)比key in obj或obj.hasOwnProperty(key)更可靠(不继承、不判原型链) -
Map的clear()是 O(1),普通对象需遍历Object.keys()再delete,是 O(n) - 如果键是动态生成的 Symbol 或短生命周期对象,用 Map 避免内存泄漏(普通对象无法弱引用键)
什么时候该用 Map,而不是 {}
别只看“功能多”,关键看使用模式:
立即学习“Java免费学习笔记(深入)”;
- 键不是字符串或数字?→ 必须用
Map - 需要频繁增删键,且对遍历顺序敏感?→
Map更稳 - 要快速知道当前有多少条数据?→ 直接读
map.size,不用Object.keys(obj).length - 做缓存或映射表,键是 DOM 元素、React 组件实例、Class 实例?→
Map是唯一安全选择 - 只是静态配置、JSON-like 结构、需要 JSON 序列化?→ 还是用
{},更轻量、可序列化、工具链友好
Map 不是对象的“升级版”,而是解决一类特定问题的工具。很多人一上来就用 Map 替换所有对象,结果发现调试困难(DevTools 显示不如对象直观)、不能用点语法、无法被 JSON.stringify 直接处理——这些都不是缺陷,而是设计取舍。











