在 JavaScript 中,new Map 和普通对象 {} 都是键值对集合,但它们有重要区别:

1. 键的类型

Map - 键可以是任意类型

1
2
3
4
5
6
7
8
9
10
11
12
const map = new Map();

// 各种类型的键都可以
map.set('string', '字符串键');
map.set(123, '数字键');
map.set(true, '布尔键');
map.set({}, '对象键');
map.set([], '数组键');
map.set(function() {}, '函数键');

console.log(map.get(123)); // '数字键'

对象 - 键只能是字符串或 Symbol

1
2
3
4
5
6
7
8
9
10
const obj = {};

obj['string'] = '字符串键'; // ✅
obj[123] = '数字键'; // 会被转换为字符串 "123"
obj[{}] = '对象键'; // 键会被转换为 "[object Object]"
obj[[1,2]] = '数组键'; // 键会被转换为 "1,2"

console.log(obj);
// { "123": "数字键", "string": "字符串键", "[object Object]": "对象键", "1,2": "数组键" }

2. 顺序保证

Map - 保持插入顺序

1
2
3
4
5
6
7
8
9
10
const map = new Map();
map.set('z', 1);
map.set('a', 2);
map.set('m', 3);

// 遍历时保持插入顺序
for (let [key, value] of map) {
console.log(key); // z, a, m (插入顺序)
}

对象 - 不保证顺序(现代JS有改进但仍有差异)

1
2
3
4
5
6
7
8
9
10
11
const obj = {
z: 1,
a: 2,
m: 3
};

// 现代JS中字符串键通常按创建顺序,但有特殊情况
for (let key in obj) {
console.log(key); // 顺序可能因JS引擎而异
}

3. 大小获取

Map - 有 size 属性

1
2
3
4
5
6
const map = new Map();
map.set('a', 1);
map.set('b', 2);

console.log(map.size); // 2

对象 - 需要手动计算

1
2
3
const obj = { a: 1, b: 2 };
console.log(Object.keys(obj).length); // 2

4. 性能差异

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Map 在频繁增删时性能更好
const map = new Map();
const obj = {};

// 添加大量数据测试
console.time('Map添加');
for (let i = 0; i < 100000; i++) {
map.set(i, i);
}
console.timeEnd('Map添加');

console.time('对象添加');
for (let i = 0; i < 100000; i++) {
obj[i] = i;
}
console.timeEnd('对象添加');

5. 方法和特性对比

Map 方法

1
2
3
4
5
6
7
8
9
const map = new Map();

map.set('key', 'value'); // 设置
map.get('key'); // 获取
map.has('key'); // 检查存在
map.delete('key'); // 删除
map.clear(); // 清空
map.forEach((value, key) => {}); // 遍历

对象方法

1
2
3
4
5
6
7
8
const obj = {};

obj.key = 'value'; // 设置
obj.key; // 获取
'key' in obj; // 检查存在
delete obj.key; // 删除
Object.keys(obj).forEach(key => {}); // 遍历

6. 原型链问题

Map - 没有原型链干扰

1
2
3
const map = new Map();
console.log(map.get('toString')); // undefined

对象 - 有原型链

1
2
3
4
5
6
7
const obj = {};
console.log(obj.toString); // ƒ toString() { [native code] }

// 安全创建无原型对象
const safeObj = Object.create(null);
console.log(safeObj.toString); // undefined

7. 序列化

对象 - 可 JSON 序列化

1
2
3
const obj = { a: 1, b: 2 };
console.log(JSON.stringify(obj)); // '{"a":1,"b":2}'

Map - 不能直接序列化

1
2
3
4
5
6
const map = new Map([['a', 1], ['b', 2]]);
console.log(JSON.stringify(map)); // '{}'

// 需要转换
console.log(JSON.stringify([...map])); // '[["a",1],["b",2]]'

8. 使用场景总结

使用 Map 的情况:

  • 键类型多样(对象、函数等作为键)
  • 需要频繁增删键值对
  • 需要保持插入顺序
  • 大数据量操作

使用对象的情况:

  • 键都是字符串或 Symbol
  • 需要 JSON 序列化
  • 简单的配置、数据存储
  • 方法和属性的集合
1
2
3
4
5
6
7
8
9
10
// Map 适合的场景
const userSessions = new Map(); // 用户会话存储,键是用户对象

// 对象适合的场景
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retryCount: 3
};

选择依据:如果需要复杂键或高性能操作,用 Map;如果是简单数据结构,用对象。