【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 更优的选择。