Map与普通对象本质不同:Map专为键值映射设计,支持任意类型键、严格插入顺序、无原型干扰、原生size和迭代;普通对象是运行时实体容器,键仅限字符串/Symbol,存在隐式转换、顺序不可控、原型污染及统计/迭代不便等问题。

Map 和普通对象({})都能存键值对,但它们不是“换种写法”的关系,而是为不同目的设计的两类结构:Map 是专为高效、灵活的键值映射而生;普通对象本质是运行时的实体容器,键值只是它的副产品。
键的类型自由度完全不同
普通对象的键只能是字符串或 Symbol。其他类型会隐式转成字符串——比如 obj[1] 和 obj['1'] 指向同一个位置,obj[{}] 实际变成 obj['[object Object]'],极易误覆盖。
Map 的键没有限制:函数、数组、null、NaN、日期、自定义对象……全都可以作为独立键使用,且彼此不转换、不混淆。
-
const map = new Map(); map.set({}, 'a'); map.set({}, 'b');→ 存储两个不同键,size 为 2 -
const obj = {}; obj[{}] = 'a'; obj[{}] = 'b';→ 实际只保留一个属性,因为两次都转成'[object Object]'
插入顺序与遍历行为稳定可靠
Map 严格按插入顺序保存和返回键值对,for...of、map.keys()、map.entries() 全部遵循该顺序,无需额外处理。
普通对象虽在 ES2015+ 后对字符串/Symbol 键保留插入序,但数字字符串键(如 '10'、'2')会被按数值升序排到前面,导致顺序不可控。想靠对象保证顺序,就得绕开数字键或手动排序。
- Map 中
set('2', 1).set('10', 2).set('a', 3)→ 遍历时顺序固定为'2' → '10' → 'a' - 对象中
{'2': 1, '10': 2, 'a': 3}→Object.keys()返回['2', '10', 'a'](数值键优先),但{a:1,'2':2,'10':3}可能返回['a','2','10'],行为依赖初始化方式
操作语义清晰,无原型干扰
Map 是纯净的数据结构:没有原型链上的预设属性,.has()、.get()、.delete() 等方法语义明确,不会和业务键名冲突。
普通对象继承 Object.prototype,自带 toString、constructor、hasOwnProperty 等方法名。一旦你用这些名字作键(比如 obj.toString = 'custom'),就可能破坏对象自身行为,或引发难以排查的 bug。
- 用
obj.hasOwnProperty判断键存在?得写obj.hasOwnProperty.call(obj, 'key')避免被覆盖 - Map 直接
map.has('key'),干净、安全、意图直白 - 清空数据:
map.clear()一键完成;对象需逐个delete或重新赋值{}
大小统计与迭代更直接
Map 提供原生 size 属性,随时获取当前键值对数量,不依赖遍历计算。
普通对象没有 size,必须用 Object.keys(obj).length,这仅统计可枚举自有属性,若含不可枚举键或 Symbol 键,结果就不准确。
- Map 迭代器天然支持
for...of,每次返回[key, value]数组,开箱即用 - 对象要迭代键值对,得先调
Object.entries(obj)转成数组,多一次内存分配和遍历 - Map 的
forEach回调参数顺序是(value, key, map),和数组习惯一致;对象没原生 forEach,得自己封装

















