在JavaScript里处理键值映射,大多数人第一反应是普通对象Object,但在ES6之后语言原生提供了Map结构。二者虽然都能存“键对应值”的数据,但在底层实现、键的类型约束和遍历行为上有本质不同。搞清楚这些差异,是写出健壮代码的前提。

键类型的支持差异
Object的键名在存储时会被强制转换为字符串,或者Symbol类型。如果你用数字、对象或者函数作为Object的键,引擎会先调用其toString逻辑。例如使用数字键100存入值,实际键变成了字符串"100",用obj[100]和obj["100"]访问的是同一项。更复杂的是,当使用普通对象作为键时,它会被转成类似"[object Object]"的字符串,这会让不同的对象键互相覆盖,造成隐蔽的逻辑错误。
Map则允许任意类型的值作为键,包括对象、函数、基本类型,且不会发生类型转换。这意味着你可以直接用DOM节点做键来关联元数据,而不必担心字符串冲突。Map内部使用基于哈希或类似有序结构的算法来识别键,对象键是通过引用比较的,两个内容相同但引用不同的对象会被视为不同键。下面的代码展示了二者的关键区别:
const obj = {};
const keyObj = { id: 1 };
obj[keyObj] = 'value';
console.log(obj['[object Object]']); // 输出 'value'
const map = new Map();
const keyA = { id: 1 };
const keyB = { id: 1 };
map.set(keyA, 'a');
map.set(keyB, 'b');
console.log(map.get(keyA)); // 输出 'a'
console.log(map.get(keyB)); // 输出 'b'
console.log(map.size); // 输出 2,两个对象引用不同
从内存和垃圾回收角度看,Map对对象键的引用是“可感知”的。当Map中的对象键不再被其他位置引用时,如果使用的是WeakMap还能自动释放,而普通Object如果用了对象做键且被转成了字符串,原对象可能仍因某些引用无法回收。在大型单页应用中,滥用Object存对象键容易造成内存泄漏。
遍历顺序与迭代能力
早期JavaScript规范并未保证Object属性的遍历顺序,虽然现代引擎对整数键、字符串键、Symbol键有了大致的先后规则,但依然容易让开发者误判。Object自身没有统一的迭代接口,需要用Object.keys、Object.values或for...in配合hasOwnProperty过滤原型链属性,写法啰嗦且容易踩坑。
Map天然实现了迭代器协议,可以用for...of直接遍历,并且严格按照插入顺序返回每一项。它提供了keys、values、entries三个方法,也支持展开运算符和数组解构。对于需要稳定顺序的队列映射、LRU缓存等结构,Map明显更合适。以下示例对比了二者遍历方式:
const obj = { b: 2, a: 1, c: 3 };
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
console.log(key, obj[key]);
}
}
const map = new Map([['b', 2], ['a', 1], ['c', 3]]);
for (const [key, value] of map) {
console.log(key, value);
}
// 输出顺序始终为 b,2 a,1 c,3
另外,Map的size属性是O(1)获取长度,而Object必须手动调用Object.keys(obj).length,在频繁统计数量的场景里前者效率更高。当数据量上升到数万条时,这种API层面的便捷性会显著影响代码清晰度和运行开销。
性能特征与适用场景
在频繁增删键值对的场景下,Map通常比Object表现更好。Object在执行delete操作时会触发引擎隐藏类的调整,可能导致后续属性访问变慢;而Map的删除是结构内平衡操作,性能曲线更平滑。对于只做一次性初始化、少量读取的配置对象,Object语法更轻量,字面量写法也更直观。
如果你的业务是构建运行时缓存、事件处理器映射、或者需要以非字符串身份做索引的数据表,优先选用Map。反之,若数据结构固定、需要和JSON相互序列化,Object仍是无法替代的,因为Map不能直接被JSON.stringify处理,必须手动转换。下面演示了Map与数组互转以满足JSON传输需求:
const map = new Map();
map.set('name', 'test');
map.set('count', 10);
// Map转数组再转JSON
const arr = Array.from(map.entries());
const json = JSON.stringify(arr);
console.log(json); // [["name","test"],["count",10]]
// 还原
const parsed = JSON.parse(json);
const restored = new Map(parsed);
console.log(restored.get('count')); // 10
总结来看,Object适合静态结构、序列化友好和写法简短的场景;Map适合动态键、任意类型键和强调遍历顺序的逻辑。团队在代码评审时应当关注是否误用Object存储大规模动态映射,这种误用往往是性能瓶颈和bug的源头。
MapObjectJavaScript修改时间:2026-08-18 18:20:37