`Map` 对象

`Map` 对象

本文将讲解 Map 对象。

我们将从基础操作到实际应用示例,循序渐进地进行讲解,帮助你在真实场景中加以运用。

YouTube Video

Map 对象

Map 是一种存储键值对的集合。它类似于对象,但不同之处在于可以使用任何类型(如对象、函数或原始类型)作为键,并且会保留插入顺序。

Map 的基础

首先,让我们看看如何创建一个 Map 并进行基本操作。

下面的代码是一个最小示例,用于创建一个空的映射、添加键并获取值。

1// Create an empty Map and add key-value pairs
2const m = new Map();
3m.set('a', 1);
4m.set('b', 2);
5
6console.log(m.get('a')); // 1
7console.log(m.size);     // 2
  • 在这段代码中,你可以使用 set 添加元素,使用 get 获取值,并通过 size 查看元素个数。
  • Map 保持插入顺序,非常适合需要顺序处理的场景。

setgethasdelete 的行为

以下是常见的读取、写入、检查存在和删除操作示例。

通过下面的代码,可以查看每个方法的返回值和对应效果。

 1// Demonstrate set, get, has, and delete
 2const m2 = new Map();
 3m2.set('x', 10);
 4console.log(m2.has('x'));  // true
 5console.log(m2.get('y'));  // undefined
 6
 7m2.delete('x');
 8console.log(m2.has('x'));  // false
 9
10// set returns the map itself, allowing method chaining
11m2.set('a', 1).set('b', 2);
12console.log(m2);  // Map { 'a' => 1, 'b' => 2 }
  • 如果键不存在,get 将返回 undefined。“has”用来检查键是否存在,“delete”则用于删除键。
  • 此外,由于 set 返回 Map 本身,因此支持方法链式调用。

任何类型都可以作为键(使用对象作为键)

“Map”的一个主要优点是可以直接使用对象作为键。

下面的示例演示了如何使用对象作为键,在 Map 中关联值。

 1// Use objects as keys in a Map
 2const keyObj = { id: 1 };
 3const keyFunc = () => {};
 4const objMap = new Map();
 5
 6// Another object with the same content but a different reference
 7const anotherKeyObj = { id: 1 };
 8
 9objMap.set(keyObj, 'objectValue');
10objMap.set(keyFunc, 'functionValue');
11objMap.set(anotherKeyObj, 'anotherValue');
12
13console.log(objMap.get(keyObj));         // 'objectValue'
14console.log(objMap.get(keyFunc));        // 'functionValue'
15console.log(objMap.get(anotherKeyObj));  // 'anotherValue'
  • 当使用对象作为键时,重要的是它们必须是相同的引用。即使内容相同,引用不同的对象也会被视为不同的键。

遍历(循环)

Map 保持插入顺序,因此经常使用遍历。

下面我们演示如何使用 for...offorEach 以及 keys()values()entries() 方法。

 1// Iterating a Map with for...of and forEach
 2const iterMap = new Map([['a', 1], ['b', 2], ['c', 3]]);
 3
 4// for...of over entries (default)
 5for (const [key, value] of iterMap) {
 6  console.log(key, value);
 7}
 8
 9// forEach callback
10iterMap.forEach((value, key) => {
11  console.log(key, value);
12});
13
14// keys() and values()
15console.log([...iterMap.keys()]);   // ['a','b','c']
16console.log([...iterMap.values()]); // [1,2,3]
  • entries() 返回 [key, value] 对数组,可以使用展开语法将其转换为数组。注意,forEach 的回调参数顺序是 value, key

MapObject 之间的转换

你可以将已有对象转换为 Map,或将 Map 转换为普通对象或数组。

 1// Convert between Map and Object / Array
 2const obj = { a: 1, b: 2 };
 3const mapFromObj = new Map(Object.entries(obj)); // Object -> Map
 4console.log(mapFromObj.get('a')); // 1
 5
 6const objFromMap = Object.fromEntries(mapFromObj); // Map -> Object
 7console.log(objFromMap); // { a: 1, b: 2 }
 8
 9const arrayFromMap = [...mapFromObj]; // Map -> Array of [key, value]
10console.log(arrayFromMap); // [['a',1], ['b',2]]
  • 使用 Object.entriesObject.fromEntries 可以轻松完成转换。但请注意,对象的键只能是字符串或 symbol,所以在转换回对象时,非字符串键会丢失。

实用模式:频率统计(计数 Map)

在统计数组中元素的频率时,使用“Map”可以让过程更简单。

下面的代码演示了如何使用 map 统计数组中字符串的出现频率并进行排序。

1// Count frequencies with Map
2const arr = ['apple','banana','apple','orange','banana','apple'];
3const freq = new Map();
4
5for (const item of arr) {
6  freq.set(item, (freq.get(item) || 0) + 1);
7}
8
9console.log([...freq.entries()]); // [['apple',3], ['banana',2], ['orange',1]]
  • 使用 Map 可以方便地检查元素是否存在并进行更新。这也可以用对象实现,但在使用任意类型作为键时,Map 更加直观。

MapJSON.stringify(序列化)注意事项

JSON.stringify 不能直接序列化 Map。如果需要保存 Map,必须先进行转换。

下面的示例展示了如何将 Map 转换为数组再转为 JSON,以及如何恢复。

1// Serialize and deserialize a Map
2const m3 = new Map([['x', 1], ['y', 2]]);
3const json = JSON.stringify([...m3]); // convert to array first
4console.log(json); // '[["x",1],["y",2]]'
5
6const restored = new Map(JSON.parse(json));
7console.log(restored.get('x')); // 1
  • 需要保存或传输的 Map 应该在序列化前转换为数组。恢复时,先用 JSON.parse 转换成数组,再还原为 Map。

WeakMap 简介及用法

WeakMap 的不同之处在于其键是弱引用(会被垃圾回收)。

它适用于用对象作为键存储缓存或元数据,当键对象被回收时会自动释放对应数据。

1// WeakMap for metadata tied to object lifecycle
2const wm = new WeakMap();
3let obj = {};
4wm.set(obj, { meta: 'info' });
5console.log(wm.get(obj)); // { meta: 'info' }
6
7obj = null; // now the object can be GC'd and its entry removed from WeakMap
  • WeakMap 不能被遍历或检查大小,但有助于防止内存泄漏。

总结

Map 是一种方便的集合,不同于对象,可以使用任意类型作为键,并且保持插入顺序。掌握从基础操作到高级用法,可以让你更灵活、直观地管理数据。根据不同的使用场景合理选择 ObjectMap,可以大大提升代码的清晰度和可读性。

您可以在我们的YouTube频道上使用Visual Studio Code跟随上述文章进行学习。 请也查看我们的YouTube频道。

YouTube Video