【JS】Map和Object的区别
JavaScript 中的 Map
和 Object
都可以用来存储键值对,但它们在设计、性能和使用场景上有显著的区别。理解这些差异有助于在开发中做出更合适的选择。
1. 键的类型
Object
:- 键只能是字符串或Symbol。
- 如果使用其他类型的值作为键(如数字、对象),它们会被自动转换为字符串。
const obj = {}; obj[1] = 'number key'; obj[{a: 1}] = 'object key'; console.log(obj['1']); // 'number key' (1 被转为字符串 '1') console.log(obj['[object Object]']); // 'object key' (对象被转为字符串 '[object Object]')
Map
:- 键可以是任意类型:字符串、数字、布尔值、函数、对象、
null
、undefined
等。 - 键的类型和值保持原样,不会发生类型转换。
const map = new Map(); map.set(1, 'number key'); map.set({a: 1}, 'object key'); map.set('str', 'string key'); console.log(map.get(1)); // 'number key' console.log(map.get({a: 1})); // undefined! (因为是不同对象引用) // 但如果用同一个对象引用作为键,则可以获取 const keyObj = {a: 1}; map.set(keyObj, 'same object key'); console.log(map.get(keyObj)); // 'same object key'
- 键可以是任意类型:字符串、数字、布尔值、函数、对象、
2. 原型与继承
Object
:- 所有普通对象都继承自
Object.prototype
。 - 这意味着对象上有一些默认的、可枚举的属性(如
toString
,hasOwnProperty
等),虽然它们通常在for...in
循环中被跳过,但在某些情况下可能会引起命名冲突或需要额外检查。 - 可以创建无原型的对象 (
Object.create(null)
) 来避免继承问题。
- 所有普通对象都继承自
Map
:Map
是一个纯粹的键值对集合,没有原型链上的默认属性。- 它不会与用户定义的键名发生冲突,更加“干净”。
3. 迭代
Object
:- 本身不可迭代,不能直接用于
for...of
循环。 - 需要通过
Object.keys()
,Object.values()
,Object.entries()
获取数组后进行迭代。 - 或者使用
for...in
循环(但会遍历所有可枚举属性,包括继承的)。for (const key of Object.keys(obj)) { ... } for (const [key, value] of Object.entries(obj)) { ... }
- 本身不可迭代,不能直接用于
Map
:- 是可迭代对象,可以直接用于
for...of
循环。 - 按照插入顺序遍历键值对。
- 提供了
.keys()
,.values()
,.entries()
方法,返回迭代器。for (const [key, value] of map) { ... } for (const key of map.keys()) { ... }
- 是可迭代对象,可以直接用于
4. 获取大小
Object
:- 没有内置的
.size
属性。 - 需要手动计算:
Object.keys(obj).length
。
- 没有内置的
Map
:- 有内置的
.size
属性,直接返回键值对的数量。console.log(map.size); // 直接获取大小
- 有内置的
5. 性能
Object
:- 对于少量、静态的键值对,性能良好。
- 当键的数量非常大时,频繁的增删改查操作性能可能不如
Map
,因为Map
是专门为频繁操作优化的数据结构。
Map
:- 在频繁的添加、删除和查找操作中性能通常更优,尤其是在键值对数量很多时。
- 浏览器引擎对
Map
的内部实现进行了优化。
6. 序列化与解析
Object
:- 可以轻松地使用
JSON.stringify()
和JSON.parse()
进行序列化和反序列化。
- 可以轻松地使用
Map
:- 不能直接被
JSON.stringify()
序列化(会得到{}
)。 - 需要先转换为数组(如
Array.from(map)
或[...map]
),然后再序列化。反序列化后需要重新构造Map
。
- 不能直接被
7. 初始化方式
Object
:- 可以通过对象字面量
{}
或new Object()
创建。 - 字面量方式简洁直观。
- 可以通过对象字面量
Map
:- 必须使用
new Map()
创建。 - 可以传入一个可迭代对象(如数组)来初始化:
const map = new Map([ ['key1', 'value1'], ['key2', 'value2'] ]);
- 必须使用
8. 删除操作
Object
:- 使用
delete obj.key
或delete obj['key']
,性能相对较差。
- 使用
Map
:- 使用
.delete(key)
方法,性能更好。
- 使用
何时使用?
场景 | 推荐使用 |
---|---|
存储具有逻辑结构的数据,属性名是字符串 | ✅ Object |
需要 JSON 序列化/反序列化 | ✅ Object |
键是字符串或 Symbol,且数量不多 | ✅ Object |
键是任意类型(如对象、函数) | ✅ Map |
需要频繁增删改查大量键值对 | ✅ Map |
需要按插入顺序遍历 | ✅ Map |
需要知道键值对的数量 (size ) |
✅ Map |
避免原型污染或默认属性干扰 | ✅ Map |
总结
Object
更适合表示实体和配置,是 JavaScript 最基础的数据结构。Map
更适合表示集合和字典,是一个为高性能键值对操作而设计的专用集合类型。
选择 Map
还是 Object
应根据具体的使用场景、键的类型、性能需求和功能要求来决定。在现代 JavaScript 开发中,Map
在许多场景下是比 Object
更优的选择。