键控集合

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

Map

Map 对象

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

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

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 和 Map 的比较

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

  • Object 的键是字符串符号,而 Map 的键可以是任何值。
  • 你可以轻松获取 Mapsize,而对于 Object,你必须手动跟踪大小。
  • Map 的迭代是按照元素的插入顺序进行的。
  • 一个 Object 有一个原型,所以映射中存在默认键。(这可以通过使用 map = Object.create(null) 来绕过。)

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

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

WeakMap 对象

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

WeakMap 的 API 与 Map 的 API 基本相同。然而,WeakMap 不允许观察其键的活跃性,这就是它不允许枚举的原因。因此,没有方法可以获取 WeakMap 中键的列表。如果存在,该列表将取决于垃圾回收的状态,从而引入不确定性。

有关更多信息和示例代码,另请参阅 WeakMap 参考页面上的“为什么选择 WeakMap?”。

WeakMap 对象的一个用例是存储对象的私有数据,或隐藏实现细节。在以下示例中,私有数据和方法属于对象内部,并存储在 privates 对象中,该对象是一个 WeakMap。实例和原型上公开的一切都是公共的;其他一切都无法从外部访问,因为 privates 未从模块导出。

js
const privates = new WeakMap();

export default 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`
  // …
};

注意: 此用例现在可以通过类和私有字段实现。

Set

Set 对象

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

以下代码展示了 Set 的一些基本操作。另请参阅 Set 参考页面以获取更多示例和完整的 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"

Array 和 Set 之间的转换

你可以使用 Array.from展开语法从 Set 创建一个 Array。此外,Set 构造函数接受一个 Array 进行反向转换。

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

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

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

Array 和 Set 的比较

传统上,在许多情况下,元素集合在 JavaScript 中存储在数组中。然而,Set 对象有一些优势

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

WeakSet 对象

WeakSet 对象是可垃圾回收值的集合,包括对象和未注册的符号WeakSet 中的一个值只能出现一次。它在 WeakSet 集合中是唯一的。

Set 对象的主要区别在于

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

WeakSet 对象的用例是有限的。它们不会造成内存泄漏,因此安全地使用 DOM 元素作为键并标记它们以进行跟踪等目的会很有用。

Map 和 Set 的键值相等性

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

  • 相等性类似于全等比较运算符 ===
  • -0+0 被认为是相等的。
  • NaN 被认为与自身相等(与 === 相反)。