文章目录
JavaScript 中的 Map 和对象(Object)都是用于存储键值对的数据结构,但它们在底层实现、性能、适用场景等方面存在显著区别。本文将深入探讨它们的异同,并分析
for (let [key, value] of map)
和for (let [key, value] of map.entries())
语法是否等效。
一、Map 与对象的核心区别
1. 语法与声明方式
在 JavaScript 中,声明一个对象通常使用大括号 {}
,而 Map 需要通过 new Map()
来创建:
// 创建对象
const obj = {
key: 'value' };
// 创建 Map
const map = new Map();
map.set('key', 'value');
对象的键通常是字符串(也可以是 Symbol),而 Map 允许任何数据类型作为键,包括对象、函数等:
const objKey = {
};
const mapExample = new Map();
mapExample.set(objKey, 'object as key');
console.log(mapExample.get(objKey)); // "object as key"
对象的键会被强制转换为字符串,而 Map 则保持键的类型,这使得 Map 在存储复杂数据时更具优势。
2. 键的存储顺序
对象的键是无序的,而 Map 的键是有序的,意味着它会按照插入顺序迭代键值对:
const objExample = {
b: 1, a: 2 };
console.log(Object.keys(objExample)); // 可能输出 ["b", "a"] 或 ["a", "b"]
const mapExample2 = new Map();
mapExample2.set('b', 1);
mapExample2.set('a', 2);
console.log([...mapExample2.keys()]); // 输出 ["b", "a"]
在某些情况下,对象的键顺序可能与插入顺序不同,而 Map 总是按照插入顺序维护键值对,这在需要顺序遍历时非常重要。
3. 迭代方式的差异
对象的键可以使用 Object.keys()
、Object.values()
和 Object.entries()
获取,然后结合 forEach
或 for...of
进行迭代:
const objSample = {
a: 1, b: 2 };
Object.entries(objSample).forEach(([key, value]) => {
console.log(key, value);
});
Map 提供了更直接的迭代方式,如 map.keys()
、map.values()
和 map.entries()
,并且可以直接用 for...of
遍历:
const mapSample = new Map([
['a', 1],
['b', 2]
]);
for (let [key, value] of mapSample) {
console.log(key, value);
}
Map 直接支持迭代,而对象需要借助 Object.entries()
才能进行类似的迭代。
二、Map 和对象的适用场景
1. 何时使用对象(Object)
- 需要简单的键值对存储,且键始终是字符串或 Symbol 时
- 作为 JSON 数据的一部分进行序列化和传输
- 需要继承
Object.prototype
的方法,例如hasOwnProperty()
、toString()
2. 何时使用 Map
- 需要高效的键值对查找和删除操作
- 需要以任意类型(对象、函数等)作为键
- 需要保证键值对的插入顺序
- 需要更直观的迭代方式
三、Map 的 for (let [key, value] of map)
与 for (let [key, value] of map.entries())
是否相同
在 JavaScript 的 Map
中,for (let [key, value] of map)
和 for (let [key, value] of map.entries())
是等价的。
1. for...of
遍历 Map
当直接对 map
进行 for...of
迭代时,默认行为是遍历 map.entries()
,即键值对:
const mapExample = new Map([
['a', 1],
['b', 2],
]);
for (let [key, value] of mapExample) {
console.log(key, value);
}
// 输出
// a 1
// b 2
实际上,Map
的默认迭代器就是 map.entries()
,因此 for (let [key, value] of map)
本质上等同于 for (let [key, value] of map.entries())
。
2. 显式调用 map.entries()
for (let [key, value] of mapExample.entries()) {
console.log(key, value);
}
// 输出
// a 1
// b 2
3. 验证 map[Symbol.iterator]
默认等于 map.entries()
我们可以通过 Symbol.iterator
验证 Map
的默认迭代行为:
console.log(mapExample[Symbol.iterator] === mapExample.entries); // true
由于 Map
的 Symbol.iterator
方法默认返回 entries()
,因此 for (let [key, value] of map)
和 for (let [key, value] of map.entries())
具有完全相同的效果。
四、注意事项
1. Map 的性能优势
在大数据量的情况下,Map
通常比对象性能更优,因为 Map
使用哈希表实现,查找和删除操作的时间复杂度通常为 O(1),而对象的查找可能涉及哈希冲突或原型链查找,可能更慢。
2. Map 不能使用 JSON.stringify()
如果你尝试对 Map
使用 JSON.stringify()
,会得到 undefined
,因为 Map
不是原生的 JSON 格式:
const mapData = new Map([
['name', 'Alice'],
['age', 25]
]);
console.log(JSON.stringify(mapData)); // "{}"
若要序列化 Map
,可以先转换为数组:
console.log(JSON.stringify([...mapData])); // '[["name","Alice"],["age",25]]'
3. 对象的 hasOwnProperty
适用于检测属性
如果你需要检查某个键是否存在于对象中,hasOwnProperty()
是一个常用方法:
const objSample = {
a: 1 };
console.log(objSample.hasOwnProperty('a')); // true
console.log(objSample.hasOwnProperty('b')); // false
但在 Map
中,我们应使用 has()
方法:
const mapSample = new Map([['a', 1]]);
console.log(mapSample.has('a')); // true
console.log(mapSample.has('b')); // false
推荐: