Map in ES6 JavaScript

了解 ES6 JavaScript 中的内建数据结构: Map

前言

JavaScript ES6 中有一个用法与对象近似的数据结构我一直不是很清楚用途。—— Map,这篇文章将比较常见的对象与 Map 以辨别 Map 的特性与使用时机。

语法

// Init 建立
const map = new Map([
[1, 'apple'],
[2, 'orange'],
]);
// Set - 新增 key 与 value
map.set(3, 'banana');
// Get - 根据 key 取 value
map.get(3);
// Delete - 根据 key 删除
map.delete(3);
// Clear - 清空所有数据
map.clear();
// Has - 检查 key 是否存在
map.has(2);
// Keys - 获取所有 key
map.keys();
// Values - 获取所有 Value
map.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 与 value
const swapMap = Array.from(map).reduce((acc, [key, value]) => acc.set(value, key), new Map());

相较对象来说 Map 的优点

  1. 没有历史遗留问题 :除了特别储存进去的键之外 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) {
}
  1. 允许任何类型的键Map 的键可以是任何类型,包括对象、函数以及原始类型(字符串、数字等)。相较之下,对象的键只能是字符串或符号(Symbol🔗)。

  2. 明确的顺序 :Map 保留键值对的插入顺序,这使得迭代时能够按照插入顺序进行,而对象则无法保证。这样明确顺序的特性也让 Map 的遍历效率更快更好。详细可以看看 builder.io 的在线测验🔗

  3. 安全性 :设置用户提供的 键/值 配对到对象上可能让人恶意向程序中注入恶意构造的对象(Object Injection Attacts🔗),从而改变程序的逻辑或执行未预期的操作,而使用 Map 的方法则可以安全的避免这种情况。

相较对象来说 Map 的缺点

  1. 学习曲线:虽然 Map 使用方法简单,但出现频率相对较少,因此不了解的人可能需要额外的学习成本。
  2. 内存开销:由于 Map 的实现细节,储存同样数量的键值对时,Map 通常比对象占用更多的内存。
  3. JSON 支持:对象能够直接被转换为 JSON 格式,Map 则需要额外的处理才能序列化为 JSON 格式。
// Object 转换为 JSON
const obj = { key: 'value' };
const jsonString = JSON.stringify(obj);
// Map 转换为 JSON
const map = new Map();
map.set('key', 'value');
// 需要转换为对象或数组后才能转换为 JSON
const mapToObject = Object.fromEntries(map);
const mapToJsonString = JSON.stringify(mapToObject);

使用时机

总结来说可以把 Map 当作是用来频繁读写的对象,它具备更好的性能、更明确的语法,而对象可以用作存储固定的键值对,在不需要频繁读写的情况下使用。

延伸阅读