Object键只能是字符串或Symbol,其他类型会转为字符串导致冲突;Map支持任意类型键且不转换,适合缓存等场景。
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 不做类型转换,键的相等性基于 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 元素当键存其对应配置,不用担心元素被回收或键冲突。
Map 严格按插入顺序迭代,行为确定;Object 的属性遍历顺序虽在 ES2015 后也规定为插入顺序,但存在例外:
'1'、'2'、'10')会被按数值大小排序,而非插入顺序'-1')会被当作普通字符串处理例如:
const obj = { '2': 'b', '10': 'j', '1': 'a' };
console.log(Object.keys(obj)); // ['1', '2', '10'] —— 数值排序,不是插入顺序
Map 没有这类陷阱,map.forEach 或 for...of 总是按写入顺序执行。
选 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。