原文: 《Why Is JavaScript’s Map Better Than Object?》[1]
作者:Hui[2]
JavaScript 提供了 Map 和 Object 来存储键值对,但是 Map 在许多场景下具有显著的优势。
1. Key 类型的灵活性
1.1 键的范围
Object:
对象键 只能是字符串或符号。其他类型(例如对象、函数和数字)会 自动转换为字符串:
const obj = {};
const key = { id: 1 };
obj[key] = 'value'; // Key is converted to "[object Object]"
console.log(obj); // { "[object Object]": "value" }
Map:
Map 的键可以是 任何类型,包括对象、函数和 NaN:
Copyconst map = new Map();
const key = { id: 1 };
map.set(key, 'value'); // Key retains its original type
console.log(map.get(key)); // "value"
1.2 处理特殊键
使用 NaN 作为键:
Copyconst map = new Map();
map.set(NaN, 'Not a Number');
console.log(map.get(NaN)); // "Not a Number"
const obj = {};
obj[NaN] = 'Not a Number';
console.log(obj[NaN]); // "Not a Number", but internally converted to the string "NaN"
Map 可以正确识别 NaN 作为唯一键,而 Object 会将其转换为字符串。
2. 内置方法和性能
2.1 内置方法
Map 提供了更直观的 API:
Copymap.set(key, value); // Add a key-value pair
map.get(key); // Retrieve a value
map.has(key); // Check if a key exists
map.delete(key); // Remove a key-value pair
map.clear(); // Remove all entries
map.size; // Get the number of entries (no need for Object.keys(obj).length)
相比之下,Object 需要手动处理:
Copyobj[key] = value; // Add a property
obj[key]; // Retrieve a value
delete obj[key]; // Remove a property
Object.keys(obj).length; // Get the number of properties
2.2 迭代效率
Map 支持直接迭代:
Copymap.forEach((value, key) => { /* ... */ });
for (const [key, value] of map) { /* ... */ }
相反,Object 在迭代之前需要进行转换:
CopyObject.keys(obj).forEach(key => { /* ... */ });
Object.values(obj).forEach(value => { /* ... */ });
Object.entries(obj).forEach(([key, value]) => { /* ... */ });
2.3 性能比较
频繁的增删改查操作:Map 针对频繁的键值插入和删除操作做了优化。 处理大型数据集:Map 通常在内存使用和访问速度方面表现更好,尤其是动态生成的键。
3. 保留插入顺序
Map 严格维护键值对的插入顺序,非常适合顺序很重要的场景:
Copyconst map = new Map();
map.set('a', 1);
map.set('b', 2);
console.log([...map]); // [['a', 1], ['b', 2]]
对于对象,ES6+ 保证以下顺序:
数字键按升序排序。 字符串键维持插入顺序。 符号键保持插入顺序。 但是,依赖对象键顺序可能会导致兼容性问题,尤其是在较旧的 JavaScript 引擎中。
4. 避免原型污染
对象容易受到原型链污染:
Copyconst obj = {};
console.log(obj.constructor); // Outputs Object constructor
obj.hasOwnProperty('key'); // Can be overridden
Map 独立于原型链:
Copyconst map = new Map();
console.log(map.constructor); // Outputs Map constructor
map.set('hasOwnProperty', 'safe'); // Safe to use
5. 推荐使用场景
6. 代码示例
6.1 词频统计
使用Map:
Copyconst text = "apple banana apple orange";
const wordCount = new Map();
text.split(' ').forEach(word => {
wordCount.set(word, (wordCount.get(word) || 0) + 1);
});
console.log(wordCount.get('apple')); // 2
使用 Object:
Copyconst text = "apple banana apple orange";
const wordCount = {};
text.split(' ').forEach(word => {
wordCount[word] = (wordCount[word] || 0) + 1;
});
console.log(wordCount.apple); // 2
6.2 使用对象作为键
使用 Map 的正确实现:
Copyconst user1 = { id: 1 };
const user2 = { id: 2 };
const permissions = new Map();
permissions.set(user1, ['read']);
permissions.set(user2, ['write']);
console.log(permissions.get(user1)); // ['read']
使用 Object 会失败:
Copyconst user1 = { id: 1 };
const user2 = { id: 2 };
const permissions = {};
permissions[user1] = ['read']; // Key converted to "[object Object]"
permissions[user2] = ['write']; // Overwrites previous key
console.log(permissions[user1]); // ['write']
7. 结论
在以下情况下使用 Map:
使用动态或复杂的键类型 执行频繁插入或删除 保留插入顺序 快速检索已插入条目数量 避免原型链干扰
在以下情况下使用对象:
存储简单、静态的数据 需要 JSON 序列化 更喜欢使用更简洁的语法来定义键值对
在现代 JavaScript 开发中选择正确的数据结构可以显著提高代码的可维护性和性能。
引用链接
[1]
《Why Is JavaScript’s Map Better Than Object?》: https://medium.com/@hxu0407/why-is-javascripts-map-better-than-object-4a7fc25a25ea
[2]
Hui: https://medium.com/@hxu0407

優(yōu)網(wǎng)科技秉承"專業(yè)團隊、品質(zhì)服務(wù)" 的經(jīng)營理念,誠信務(wù)實的服務(wù)了近萬家客戶,成為眾多世界500強、集團和上市公司的長期合作伙伴!
優(yōu)網(wǎng)科技成立于2001年,擅長網(wǎng)站建設(shè)、網(wǎng)站與各類業(yè)務(wù)系統(tǒng)深度整合,致力于提供完善的企業(yè)互聯(lián)網(wǎng)解決方案。優(yōu)網(wǎng)科技提供PC端網(wǎng)站建設(shè)(品牌展示型、官方門戶型、營銷商務(wù)型、電子商務(wù)型、信息門戶型、微信小程序定制開發(fā)、移動端應(yīng)用(手機站、APP開發(fā))、微信定制開發(fā)(微信官網(wǎng)、微信商城、企業(yè)微信)等一系列互聯(lián)網(wǎng)應(yīng)用服務(wù)。