Object键只能是字符串或Symbol,其他类型会转为字符串导致冲突;Map支持任意类型键且不转换,适合缓存等场景。

Object 的键只能是字符串或 Symbol
JavaScript 中 Object 的键会自动转为字符串(除了 Symbol),哪怕你写 { [1]: 'a' },实际也是 { '1': 'a' }。数组、对象、函数等作为键时,统统调用 toString(),结果往往是 '[object Object]',导致意外覆盖:
const obj = {};
obj[{}] = 'first';
obj[{}] = 'second';
console.log(Object.keys(obj)); // ['[object Object]']
console.log(obj[{}]); // 'second' —— 前一个被覆盖了
Symbol 是唯一能避免自动转换的例外,但每个 Symbol() 都是唯一值,无法复用作通用键。
Map 允许任意类型作键,包括 null、NaN、对象、函数
Map 不做类型转换,键的相等性基于 SameValueZero 算法(NaN 与 NaN 视为相等,这点和 === 一致):
-
map.set({}, 'obj1')和map.set({}, 'obj2')是两个独立条目 -
map.set(NaN, 'nan')可以正确存取,map.get(NaN)返回'nan' -
map.set(null, 'null')、map.set(undefined, 'undef')都合法且互不干扰
这使得 Map 天然适合缓存、元数据绑定、事件监听器映射等场景——比如把 DOM 元素当键存其对应配置,不用担心元素被回收或键冲突。
立即学习“Java免费学习笔记(深入)”;
性能与迭代顺序:Map 更稳定,Object 有隐式排序规则
Map 严格按插入顺序迭代,行为确定;Object 的属性遍历顺序虽在 ES2015 后也规定为插入顺序,但存在例外:
- 纯数字字符串键(如
'1'、'2'、'10')会被按数值大小排序,而非插入顺序 - 负号开头的数字字符串(如
'-1')会被当作普通字符串处理 - Symbol 键总在最后
例如:
const obj = { '2': 'b', '10': 'j', '1': 'a' };
console.log(Object.keys(obj)); // ['1', '2', '10'] —— 数值排序,不是插入顺序
Map 没有这类陷阱,map.forEach 或 for...of 总是按写入顺序执行。
什么时候该用 Object,什么时候必须用 Map?
选 Object 当:
- 键固定且已知是字符串(如配置项
{ timeout: 5000, retry: 3 }) - 需要原型方法(
hasOwnProperty、isPrototypeOf)或与 JSON 互转(JSON.stringify不处理Map) - 用点语法或方括号访问更自然(
obj.name比map.get('name')少打几个字符)
必须用 Map 当:
- 键可能为非字符串(对象、函数、
null、NaN) - 需要频繁增删键值对(
Map的delete是 O(1),而delete obj[key]在某些引擎中可能触发哈希表重建) - 依赖精确的插入顺序迭代(尤其涉及数字字符串键时)
- 要获取键值对数量(
map.size是属性,Object.keys(obj).length需生成新数组)
键类型限制不是“能不能用”的问题,而是“会不会意外丢失语义”的问题。一旦你发现某个逻辑里想用 {} 或 function 当键,就别犹豫,直接上 Map。










