Map允许任意类型作键且保序,Object仅支持字符串/Symbol键且枚举顺序不固定;Map有size属性和语义明确的原生方法,无原型污染风险,适合需非字符串键、确定顺序或高频操作的场景。

Map 的键能用对象,Object 的键不能
这是最常踩坑的一点:你试图用一个 domNode 或 { id: 1 } 当作 Object 的键,结果发现所有键都被转成了 "[object Object]",值全串了。
Object 只接受字符串或 Symbol 作键,其他类型(包括数字、布尔、对象)都会被强制调用 toString() 转换;而 Map 允许任意类型作为键,且严格按引用比较——两个不同对象即使内容一样,也是两个独立的键。
const obj = {}; obj[{}] = 'a'; obj[{}] = 'b'; console.log(obj['[object Object]']); // 'b' —— 后写覆盖前写const map = new Map(); map.set({}, 'a'); map.set({}, 'b'); console.log(map.size); // 2 —— 两个不同对象,两个键
迭代顺序不一致:Map 严格保序,Object 有例外
如果你依赖“先设的属性先遍历”,用 Object 就可能出错。ES6+ 规定 Object 的枚举顺序是:数字键升序 → 字符串键插入序 → Symbol 键插入序。这意味着 {"2": "x", "1": "y"} 的 Object.keys() 返回 ["1", "2"],不是你写的顺序。
- Map 始终按
set()插入顺序返回:map.set("b",1).set("1",2).set("a",3)→Array.from(map.keys())就是["b","1","a"] - 需要 LRU 缓存、操作日志、表单字段顺序还原等场景,必须选 Map
size 属性和原生方法:Map 更省事,Object 需绕路
Object 没有 size,查长度得写 Object.keys(obj).length,但这个只统计可枚举自有属性,漏掉不可枚举键或原型链上的 key;Map 直接有 map.size,且 map.has(key)、map.delete(key)、map.clear() 全是语义明确的原生方法。
-
obj.hasOwnProperty('x')不等于obj.x !== undefined(因为值可能是undefined),而map.has(key)就是纯判断键是否存在 -
delete obj.key在频繁操作时性能较差;map.delete(key)是 O(1) 平均时间复杂度
原型污染风险:Object 天然带 toString,Map 没这问题
空对象 {} 依然继承自 Object.prototype,所以 obj.toString、obj.hasOwnProperty 等方法天然存在。一旦你存了个 obj['toString'] = 'hacked',就覆盖了原型方法,后续调用 obj.toString() 就崩了;Map 实例没有这些默认键,完全干净。
立即学习“Java免费学习笔记(深入)”;
- 用
Object.create(null)能规避,但这就已经不是普通字面量了,开发成本上升 - 服务端接收前端 JSON 数据后直接当 Object 用?没问题;但若需把用户上传的文件对象、DOM 元素、回调函数当 key 缓存,只能靠 Map
Map 不是替代品,是必要选择。别等到缓存失效、键冲突、迭代乱序才回头改——从第一次 set 开始就该想清楚。











