Map in ES6 JavaScript
了解 ES6 JavaScript 中的内建数据结构: Map
前言
JavaScript ES6 中有一个用法与对象近似的数据结构我一直不是很清楚用途。—— Map
,这篇文章将比较常见的对象与 Map
以辨别 Map
的特性与使用时机。
语法
// Init 建立const map = new Map([ [1, 'apple'], [2, 'orange'],]);
// Set - 新增 key 与 valuemap.set(3, 'banana');// Get - 根据 key 取 valuemap.get(3);// Delete - 根据 key 删除map.delete(3);// Clear - 清空所有数据map.clear();// Has - 检查 key 是否存在map.has(2);// Keys - 获取所有 keymap.keys();// Values - 获取所有 Valuemap.values();// Entries - 获取 key-value 组合map.entries();// Length - 数据长度map.size;// Iteration for - for 遍历for (const [key, value] of map) { console.log({ key, value });}// Iteration forEach - forEach 遍历map.forEach((value, key) => { console.log({ key, value });});// Swap Keys & Values - 交换 key 与 valueconst swapMap = Array.from(map).reduce((acc, [key, value]) => acc.set(value, key), new Map());
相较对象来说 Map 的优点
- 没有历史遗留问题 :除了特别储存进去的键之外
Map
并不存在任何键,相较于Object
来说更为明确简洁。
const myMap = {};
myMap.valueOf; // => [Function: valueOf]myMap.toString; // => [Function: toString]myMap.hasOwnProperty; // => [Function: hasOwnProperty]myMap.isPrototypeOf; // => [Function: isPrototypeOf]myMap.propertyIsEnumerable; // => [Function: propertyIsEnumerable]myMap.toLocaleString; // => [Function: toLocaleString]myMap.constructor; // => [Function: Object]
并且在取值时,举例来说 books[id]
取值可能会需要担心 id
键是否真的存在于 books
当中,因此需要在取值前先判断是否存在,像是:
// 一般检测对象是否具备某个键的方法if (books.hasOwnProperty(id)) {}
// 应对某些特殊情况if (Object.prototype.hasOwnProperty.call(books, id)) {}
但在 Map
可以直接使用对应的方法去查询,并且可以确保默认没有额外的键存在,看看以下简洁的语法!
myMap.get(key);
for (const [key, value] of myMap) {}
-
允许任何类型的键 :
Map
的键可以是任何类型,包括对象、函数以及原始类型(字符串、数字等)。相较之下,对象的键只能是字符串或符号(Symbol)。 -
明确的顺序 :Map 保留键值对的插入顺序,这使得迭代时能够按照插入顺序进行,而对象则无法保证。这样明确顺序的特性也让
Map
的遍历效率更快更好。详细可以看看 builder.io 的在线测验。 -
安全性 :设置用户提供的
键/值
配对到对象上可能让人恶意向程序中注入恶意构造的对象(Object Injection Attacts),从而改变程序的逻辑或执行未预期的操作,而使用Map
的方法则可以安全的避免这种情况。
相较对象来说 Map 的缺点
- 学习曲线:虽然
Map
使用方法简单,但出现频率相对较少,因此不了解的人可能需要额外的学习成本。 - 内存开销:由于 Map 的实现细节,储存同样数量的键值对时,Map 通常比对象占用更多的内存。
- JSON 支持:对象能够直接被转换为 JSON 格式,Map 则需要额外的处理才能序列化为 JSON 格式。
// Object 转换为 JSONconst obj = { key: 'value' };const jsonString = JSON.stringify(obj);
// Map 转换为 JSONconst map = new Map();map.set('key', 'value');
// 需要转换为对象或数组后才能转换为 JSONconst mapToObject = Object.fromEntries(map);const mapToJsonString = JSON.stringify(mapToObject);
使用时机
总结来说可以把 Map
当作是用来频繁读写的对象,它具备更好的性能、更明确的语法,而对象可以用作存储固定的键值对,在不需要频繁读写的情况下使用。
延伸阅读
- Map - mdn
- Use Maps more and Objects less - builder.io
- You Should Use Maps and Sets in JS - Syntax
- Map vs Object in JavaScript - Leigh Halliday