键控集合

本章介绍通过键索引的数据集合;MapSet 对象包含可按插入顺序迭代的元素。

¥This chapter introduces collections of data which are indexed by a key; Map and Set objects contain elements which are iterable in the order of insertion.

映射

¥Maps

映射对象

¥Map object

Map 对象是一个简单的键/值映射,可以按插入顺序迭代其元素。

¥A Map object is a simple key/value map and can iterate its elements in insertion order.

以下代码显示了 Map 的一些基本操作。另请参阅 Map 参考页以获取更多示例和完整的 API。你可以使用 for...of 循环为每次迭代返回 [key, value] 的数组。

¥The following code shows some basic operations with a Map. See also the Map reference page for more examples and the complete API. You can use a for...of loop to return an array of [key, value] for each iteration.

js
const sayings = new Map();
sayings.set("dog", "woof");
sayings.set("cat", "meow");
sayings.set("elephant", "toot");
sayings.size; // 3
sayings.get("dog"); // woof
sayings.get("fox"); // undefined
sayings.has("bird"); // false
sayings.delete("dog");
sayings.has("dog"); // false

for (const [key, value] of sayings) {
  console.log(`${key} goes ${value}`);
}
// "cat goes meow"
// "elephant goes toot"

sayings.clear();
sayings.size; // 0

对象和映射的比较

¥Object and Map compared

传统上,objects 用于将字符串映射到值。对象允许你将键设置为值、检索这些值、删除键以及检测键中是否存储了某些内容。然而,Map 对象还有一些优点,使它们成为更好的地图。

¥Traditionally, objects have been used to map strings to values. Objects allow you to set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key. Map objects, however, have a few more advantages that make them better maps.

  • Object 的键是 stringssymbols,而 Map 的键可以是任何值。
  • 你可以轻松获得 Mapsize,而你必须手动跟踪 Object 的尺寸。
  • 映射的迭代按照元素的插入顺序进行。
  • Object 有原型,所以地图中有默认键。(这可以使用 map = Object.create(null) 绕过。)

这三个提示可以帮助你决定是使用 Map 还是 Object

¥These three tips can help you to decide whether to use a Map or an Object:

  • 当键在运行时之前未知,并且所有键都是相同类型并且所有值都是相同类型时,请在对象上使用映射。
  • 如果需要将原始值存储为键,请使用映射,因为对象将每个键视为字符串,无论它是数字值、布尔值还是任何其他原始值。
  • 当存在对单个元素进行操作的逻辑时,请使用对象。

弱映射对象

¥WeakMap object

WeakMap 是键/值对的集合,其键必须是对象或 非注册符号,具有任意 JavaScript 类型 的值,并且不会创建对其键的强引用。也就是说,对象作为 WeakMap 中的键的存在不会阻止该对象被垃圾收集。一旦用作键的对象被收集,它在任何 WeakMap 中的对应值也将成为垃圾收集的候选者 - 只要它们在其他地方没有被强烈引用。唯一可以用作 WeakMap 键的原始类型是符号(更具体地说,是 非注册符号),因为未注册的符号保证是唯一的并且无法重新创建。

¥A WeakMap is a collection of key/value pairs whose keys must be objects or non-registered symbols, with values of any arbitrary JavaScript type, and which does not create strong references to its keys. That is, an object's presence as a key in a WeakMap does not prevent the object from being garbage collected. Once an object used as a key has been collected, its corresponding values in any WeakMap become candidates for garbage collection as well — as long as they aren't strongly referred to elsewhere. The only primitive type that can be used as a WeakMap key is symbol — more specifically, non-registered symbols — because non-registered symbols are guaranteed to be unique and cannot be re-created.

WeakMap API 本质上与 Map API 相同。然而,WeakMap 不允许观察其键的活跃度,这就是它不允许枚举的原因。所以没有办法获取 WeakMap 中的键列表。如果有的话,该列表将取决于垃圾收集的状态,从而引入非确定性。

¥The WeakMap API is essentially the same as the Map API. However, a WeakMap doesn't allow observing the liveness of its keys, which is why it doesn't allow enumeration. So there is no method to obtain a list of the keys in a WeakMap. If there were, the list would depend on the state of garbage collection, introducing non-determinism.

有关更多信息和示例代码,另请参阅 WeakMap 参考页上的 "为什么是 WeakMap?"。

¥For more information and example code, see also "Why WeakMap?" on the WeakMap reference page.

WeakMap 对象的一个用例是存储对象的私有数据,或隐藏实现细节。以下示例来自 Nick Fitzgerald 的博客文章 "使用 ECMAScript 6 WeakMap 隐藏实现细节"。私有数据和方法属于对象内部,存储在 privates 对象中,该对象是 WeakMap。实例和原型上暴露的所有内容都是公开的;其他一切都无法从外界访问,因为 privates 未从模块导出。

¥One use case of WeakMap objects is to store private data for an object, or to hide implementation details. The following example is from Nick Fitzgerald's blog post "Hiding Implementation Details with ECMAScript 6 WeakMaps". The private data and methods belong inside the object and are stored in the privates object, which is a WeakMap. Everything exposed on the instance and prototype is public; everything else is inaccessible from the outside world because privates is not exported from the module.

js
const privates = new WeakMap();

function Public() {
  const me = {
    // Private data goes here
  };
  privates.set(this, me);
}

Public.prototype.method = function () {
  const me = privates.get(this);
  // Do stuff with private data in `me`
  // …
};

module.exports = Public;

¥Sets

设置对象

¥Set object

Set 对象是唯一值的集合。你可以按插入顺序迭代其元素。Set 中的值只能出现一次;它在 Set 系列中是独一无二的。

¥Set objects are collections of unique values. You can iterate its elements in insertion order. A value in a Set may only occur once; it is unique in the Set's collection.

以下代码显示了 Set 的一些基本操作。另请参阅 Set 参考页以获取更多示例和完整的 API。

¥The following code shows some basic operations with a Set. See also the Set reference page for more examples and the complete API.

js
const mySet = new Set();
mySet.add(1);
mySet.add("some text");
mySet.add("foo");

mySet.has(1); // true
mySet.delete("foo");
mySet.size; // 2

for (const item of mySet) {
  console.log(item);
}
// 1
// "some text"

数组和集合之间的转换

¥Converting between Array and Set

你可以使用 Array.from扩展语法 从集合中创建 Array。此外,Set 构造函数接受 Array 以进行相反方向的转换。

¥You can create an Array from a Set using Array.from or the spread syntax. Also, the Set constructor accepts an Array to convert in the other direction.

注意:Set 对象存储唯一值 - 因此转换时数组中的任何重复元素都会被删除!

¥Note: Set objects store unique values—so any duplicate elements from an Array are deleted when converting!

js
Array.from(mySet);
[...mySet2];

mySet2 = new Set([1, 2, 3, 4]);

数组和集合比较

¥Array and Set compared

传统上,在很多情况下,JavaScript 中的一组元素都存储在数组中。然而,Set 对象有一些优点:

¥Traditionally, a set of elements has been stored in arrays in JavaScript in a lot of situations. The Set object, however, has some advantages:

  • 按值(arr.splice(arr.indexOf(val), 1))删除数组元素非常慢。
  • Set 对象允许你按值删除元素。对于数组,你必须根据元素的索引进行 splice
  • 在数组中找不到值 NaNindexOf
  • Set 对象存储唯一值。你不必手动跟踪重复项。

弱集对象

¥WeakSet object

WeakSet 对象是垃圾可回收值的集合,包括对象和 非注册符号WeakSet 中的值只能出现一次。它在 WeakSet 系列中是独一无二的。

¥WeakSet objects are collections of garbage-collectable values, including objects and non-registered symbols. A value in the WeakSet may only occur once. It is unique in the WeakSet's collection.

Set 对象的主要区别是:

¥The main differences to the Set object are:

  • Sets 相比,WeakSets 仅是对象或符号的集合,而不是任何类型的任意值的集合。
  • WeakSet 很弱:对集合中对象的引用保持较弱。如果没有其他对存储在 WeakSet 中的对象的引用,它们可以被垃圾收集。这也意味着集合中没有存储当前对象的列表。
  • WeakSets 不可枚举。

WeakSet 对象的用例是有限的。它们不会泄漏内存,因此可以安全地使用 DOM 元素作为键并将其标记用于跟踪目的。

¥The use cases of WeakSet objects are limited. They will not leak memory, so it can be safe to use DOM elements as a key and mark them for tracking purposes, for example.

Map 和 Set 的键和值相等

¥Key and value equality of Map and Set

Map 对象的键相等性和 Set 对象的值相等性均基于 SameValueZero 算法

¥Both the key equality of Map objects and the value equality of Set objects are based on the SameValueZero algorithm:

  • 相等的工作原理类似于恒等比较运算符 ===
  • -0+0 被认为是相等的。
  • NaN 被认为等于其自身(与 === 相反)。