集合

Set 对象允许你存储任何类型的唯一值,无论是 primitive values 还是对象引用。

¥The Set object lets you store unique values of any type, whether primitive values or object references.

描述

¥Description

Set 对象是值的集合。集合中的值只能出现一次;它在该系列的收藏中是独一无二的。你可以按插入顺序迭代集合的元素。插入顺序对应于 add() 方法成功将每个元素插入集合中的顺序(即,调用 add() 时集合中尚不存在相同的元素)。

¥Set objects are collections of values. A value in the set may only occur once; it is unique in the set's collection. You can iterate through the elements of a set in insertion order. The insertion order corresponds to the order in which each element was inserted into the set by the add() method successfully (that is, there wasn't an identical element already in the set when add() was called).

该规范要求实现 "平均而言,提供的访问时间与集合中元素的数量呈次线性关系" 集。因此,它可以在内部表示为哈希表(具有 O(1) 查找)、搜索树(具有 O(log(N)) 查找)或任何其他数据结构,只要复杂度优于 O (N)。

¥The specification requires sets to be implemented "that, on average, provide access times that are sublinear on the number of elements in the collection". Therefore, it could be represented internally as a hash table (with O(1) lookup), a search tree (with O(log(N)) lookup), or any other data structure, as long as the complexity is better than O(N).

值相等性

¥Value equality

值相等基于 SameValueZero 算法。(以前使用 SameValue,将 0-0 视为不同的。检查 浏览器兼容性。)这意味着 NaN 被认为与 NaN 相同(即使 NaN !== NaN),并且根据 === 运算符的语义,所有其他值都被认为是相等的。

¥Value equality is based on the SameValueZero algorithm. (It used to use SameValue, which treated 0 and -0 as different. Check browser compatibility.) This means NaN is considered the same as NaN (even though NaN !== NaN) and all other values are considered equal according to the semantics of the === operator.

性能

¥Performance

has 方法检查某个值是否在集合中,所使用的方法平均而言比测试先前添加到集合中的大多数元素要快。特别是,当数组的 length 等于集合的 size 时,平均而言,它比 Array.prototype.includes 方法更快。

¥The has method checks if a value is in the set, using an approach that is, on average, quicker than testing most of the elements that have previously been added to the set. In particular, it is, on average, faster than the Array.prototype.includes method when an array has a length equal to a set's size.

设置构图

¥Set composition

Set 对象提供了一些方法,允许你像数学运算一样组合集合。这些方法包括:

¥The Set object provides some methods that allow you to compose sets like you would with mathematical operations. These methods include:

方法 返回类型 数学等价 维恩图
A.difference(B) Set <数学显示="inline"><语义> A B <注释编码="TeX">A\setminus B A Venn diagram where two circles overlap. The difference of A and B is the part of A that is not overlapping B.
A.intersection(B) Set <数学显示="inline"><语义> A B <注释编码="TeX">A\cap B A Venn diagram where two circles overlap. The intersection of A and B is the part where they overlap.
A.symmetricDifference(B) Set <语义> ( A B ) ( B A ) (A\setminusB)\cup(B\setminusA) A Venn diagram where two circles overlap. The symmetric difference of A and B is the region contained by either circle but not both.
A.union(B) Set <数学显示="inline"><语义> A B <注释编码="TeX">A\cup B A Venn diagram where two circles overlap. The symmetric difference of A and B is the region contained by either or both circles.
A.isDisjointFrom(B) Boolean <数学显示="inline"><语义> A B = <注释编码="TeX">A\cap B = \empty A Venn diagram with two circles. A and B are disjoint because the circles have no region of overlap.
A.isSubsetOf(B) Boolean <数学显示="inline"><语义> A B <注释编码="TeX">A\subseteq B A Venn diragram with two circles. A is a subset of B because A is completely contained in B.
A.isSupersetOf(B) Boolean <语义> A Axis B <注释编码="TeX">A\supseteq B A Venn diagram with two circles. A is a superset of B because B is completely contained in A.

为了使它们更具通用性,这些方法不仅接受 Set 对象,还接受任何 set-like 对象。

¥To make them more generalizable, these methods don't just accept Set objects, but anything that's set-like.

类似集合的对象

¥Set-like objects

所有 集合组合方法 都要求 this 是实际的 Set 实例,但它们的参数只需类似设置即可。类集合对象是提供以下功能的对象:

¥All set composition methods require this to be an actual Set instance, but their arguments just need to be set-like. A set-like object is an object that provides the following:

  • 包含数字的 size 属性。
  • 接受一个元素并返回一个布尔值的 has() 方法。
  • 一个 keys() 方法,返回集合中的 iterator 个元素。

例如,Map 对象是类似集合的,因为它们也有 sizehas()keys(),因此在 set 方法中使用时它们的行为就像键集合:

¥For example, Map objects are set-like because they also have size, has(), and keys(), so they behave just like sets of keys when used in set methods:

js
const a = new Set([1, 2, 3]);
const b = new Map([
  [1, "one"],
  [2, "two"],
  [4, "four"],
]);
console.log(a.union(b)); // Set(4) {1, 2, 3, 4}

注意:类似集合的协议调用 keys() 方法而不是 [@@iterator]() 来生成元素。这是为了使映射成为有效的类似集合的对象,因为对于映射,迭代器会生成条目,但 has() 方法需要键。

¥Note: The set-like protocol invokes the keys() method instead of [@@iterator]() to produce elements. This is to make maps valid set-like objects, because for maps, the iterator produces entries but the has() method takes keys.

数组 不像集合,因为它们没有 has() 方法或 size 属性,并且它们的 keys() 方法生成索引而不是元素。WeakSet 对象也不像集合,因为它们没有 keys() 方法。

¥Arrays are not set-like because they don't have a has() method or the size property, and their keys() method produces indices instead of elements. WeakSet objects are also not set-like because they don't have a keys() method.

类似集合的浏览器 API

¥Set-like browser APIs

类似浏览器 Set 的对象(或 "类似集合的对象")是 Web API 接口,其行为在很多方面与 Set 类似。

¥Browser Set-like objects (or "setlike objects") are Web API interfaces that behave in many ways like a Set.

就像 Set 一样,元素可以按照添加到对象的顺序进行迭代。类似 Set 的对象和 Set 也具有共享相同名称和行为的属性和方法。然而,与 Set 不同的是,它们只允许每个条目使用特定的预定义类型。

¥Just like Set, elements can be iterated in the same order that they were added to the object. Set-like objects and Set also have properties and methods that share the same name and behavior. However unlike Set they only allow a specific predefined type for each entry.

允许的类型在规范 IDL 定义中设置。例如,GPUSupportedFeatures 是一个类似 Set 的对象,必须使用字符串作为键/值。这是在下面的 IDL 规范中定义的:

¥The allowed types are set in the specification IDL definition. For example, GPUSupportedFeatures is a Set-like object that must use strings as the key/value. This is defined in the specification IDL below:

webidl
interface GPUSupportedFeatures {
  readonly setlike<DOMString>;
};

类似 Set 的对象要么是只读的,要么是可读写的(请参阅上面 IDL 中的 readonly 关键字)。

¥Set-like objects are either read-only or read-writable (see the readonly keyword in the IDL above).

除了对条目类型的限制之外,方法和属性与 Set 中的等效实体具有相同的行为。

¥The methods and properties have the same behavior as the equivalent entities in Set, except for the restriction on the types of the entry.

以下是类似 Set 的只读浏览器对象的示例:

¥The following are examples of read-only Set-like browser objects:

以下是可写的类 Set 浏览器对象的示例:

¥The following are examples of writable Set-like browser objects:

构造函数

¥Constructor

Set()

创建一个新的 Set 对象。

静态属性

¥Static properties

Set[@@species]

用于创建派生对象的构造函数。

实例属性

¥Instance properties

这些属性在 Set.prototype 上定义并由所有 Set 实例共享。

¥These properties are defined on Set.prototype and shared by all Set instances.

Set.prototype.constructor

创建实例对象的构造函数。对于 Set 实例,初始值为 Set 构造函数。

Set.prototype.size

返回 Set 对象中的值的数量。

Set.prototype[@@toStringTag]

@@toStringTag 属性的初始值为字符串 "Set"。该属性在 Object.prototype.toString() 中使用。

实例方法

¥Instance methods

Set.prototype.add()

如果 Set 中没有具有相同值的元素,则将具有指定值的新元素插入到 Set 对象中。

Set.prototype.clear()

Set 对象中删除所有元素。

Set.prototype.delete()

删除与 value 关联的元素并返回一个布尔值,断言元素是否已成功删除。之后 Set.prototype.has(value) 将返回 false

Set.prototype.difference()

获取一个集合并返回一个新集合,其中包含该集合中但不包含给定集合中的元素。

Set.prototype.entries()

返回一个新的迭代器对象,其中按插入顺序包含 Set 对象中每个元素的 [value, value] 数组。这与 Map 对象类似,因此每个条目的键与其 Set 的值相同。

Set.prototype.forEach()

按插入顺序,对 Set 对象中存在的每个值调用 callbackFn 一次。如果提供了 thisArg 参数,它将用作每次调用 callbackFn 时的 this 值。

Set.prototype.has()

返回一个布尔值,断言 Set 对象中是否存在具有给定值的元素。

Set.prototype.intersection()

获取一个集合并返回一个新集合,其中包含该集合和给定集合中的元素。

Set.prototype.isDisjointFrom()

接受一个集合并返回一个布尔值,指示该集合是否没有与给定集合相同的元素。

Set.prototype.isSubsetOf()

接受一个集合并返回一个布尔值,指示该集合的所有元素是否都在给定集合中。

Set.prototype.isSupersetOf()

获取一个集合并返回一个布尔值,指示给定集合的所有元素是否都在该集合中。

Set.prototype.keys()

Set.prototype.values() 的别名。

Set.prototype.symmetricDifference()

获取一个集合并返回一个新集合,其中包含位于该集合或给定集合中的元素,但不同时位于两者中。

Set.prototype.union()

获取一个集合并返回一个新集合,其中包含该集合和给定集合中的一个或两个中的元素。

Set.prototype.values()

返回一个新的迭代器对象,该对象按插入顺序生成 Set 对象中每个元素的值。

Set.prototype[@@iterator]()

返回一个新的迭代器对象,该对象按插入顺序生成 Set 对象中每个元素的值。

示例

¥Examples

使用 Set 对象

¥Using the Set object

js
const mySet1 = new Set();

mySet1.add(1); // Set(1) { 1 }
mySet1.add(5); // Set(2) { 1, 5 }
mySet1.add(5); // Set(2) { 1, 5 }
mySet1.add("some text"); // Set(3) { 1, 5, 'some text' }
const o = { a: 1, b: 2 };
mySet1.add(o);

mySet1.add({ a: 1, b: 2 }); // o is referencing a different object, so this is okay

mySet1.has(1); // true
mySet1.has(3); // false, since 3 has not been added to the set
mySet1.has(5); // true
mySet1.has(Math.sqrt(25)); // true
mySet1.has("Some Text".toLowerCase()); // true
mySet1.has(o); // true

mySet1.size; // 5

mySet1.delete(5); // removes 5 from the set
mySet1.has(5); // false, 5 has been removed

mySet1.size; // 4, since we just removed one value

mySet1.add(5); // Set(5) { 1, 'some text', {...}, {...}, 5 } - a previously deleted item will be added as a new item, it will not retain its original position before deletion

console.log(mySet1); // Set(5) { 1, "some text", {…}, {…}, 5 }

迭代集

¥Iterating sets

集合上的迭代按插入顺序访问元素。

¥The iteration over a set visits elements in insertion order.

js
for (const item of mySet1) {
  console.log(item);
}
// 1, "some text", { "a": 1, "b": 2 }, { "a": 1, "b": 2 }, 5

for (const item of mySet1.keys()) {
  console.log(item);
}
// 1, "some text", { "a": 1, "b": 2 }, { "a": 1, "b": 2 }, 5

for (const item of mySet1.values()) {
  console.log(item);
}
// 1, "some text", { "a": 1, "b": 2 }, { "a": 1, "b": 2 }, 5

// key and value are the same here
for (const [key, value] of mySet1.entries()) {
  console.log(key);
}
// 1, "some text", { "a": 1, "b": 2 }, { "a": 1, "b": 2 }, 5

// Convert Set object to an Array object, with Array.from
const myArr = Array.from(mySet1); // [1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}, 5]

// the following will also work if run in an HTML document
mySet1.add(document.body);
mySet1.has(document.querySelector("body")); // true

// converting between Set and Array
const mySet2 = new Set([1, 2, 3, 4]);
console.log(mySet2.size); // 4
console.log([...mySet2]); // [1, 2, 3, 4]

// intersect can be simulated via
const intersection = new Set([...mySet1].filter((x) => mySet2.has(x)));

// difference can be simulated via
const difference = new Set([...mySet1].filter((x) => !mySet2.has(x)));

// Iterate set entries with forEach()
mySet2.forEach((value) => {
  console.log(value);
});
// 1
// 2
// 3
// 4

实现基本的集合操作

¥Implementing basic set operations

js
function isSuperset(set, subset) {
  for (const elem of subset) {
    if (!set.has(elem)) {
      return false;
    }
  }
  return true;
}

function union(setA, setB) {
  const _union = new Set(setA);
  for (const elem of setB) {
    _union.add(elem);
  }
  return _union;
}

function intersection(setA, setB) {
  const _intersection = new Set();
  for (const elem of setB) {
    if (setA.has(elem)) {
      _intersection.add(elem);
    }
  }
  return _intersection;
}

function symmetricDifference(setA, setB) {
  const _difference = new Set(setA);
  for (const elem of setB) {
    if (_difference.has(elem)) {
      _difference.delete(elem);
    } else {
      _difference.add(elem);
    }
  }
  return _difference;
}

function difference(setA, setB) {
  const _difference = new Set(setA);
  for (const elem of setB) {
    _difference.delete(elem);
  }
  return _difference;
}

// Examples
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 3]);
const setC = new Set([3, 4, 5, 6]);

isSuperset(setA, setB); // returns true
union(setA, setC); // returns Set {1, 2, 3, 4, 5, 6}
intersection(setA, setC); // returns Set {3, 4}
symmetricDifference(setA, setC); // returns Set {1, 2, 5, 6}
difference(setA, setC); // returns Set {1, 2}

与数组的关系

¥Relation to arrays

js
const myArray = ["value1", "value2", "value3"];

// Use the regular Set constructor to transform an Array into a Set
const mySet = new Set(myArray);

mySet.has("value1"); // returns true

// Use the spread syntax to transform a set into an Array.
console.log([...mySet]); // Will show you exactly the same Array as myArray

从数组中删除重复元素

¥Remove duplicate elements from an array

js
// Use to remove duplicate elements from an array
const numbers = [2, 13, 4, 4, 2, 13, 13, 4, 4, 5, 5, 6, 6, 7, 5, 32, 13, 4, 5];

console.log([...new Set(numbers)]); // [2, 13, 4, 5, 6, 7, 32]

与字符串的关系

¥Relation to strings

js
// Case sensitive (set will contain "F" and "f")
new Set("Firefox"); // Set(7) [ "F", "i", "r", "e", "f", "o", "x" ]

// Duplicate omission ("f" occurs twice in the string but set will contain only one)
new Set("firefox"); // Set(6) [ "f", "i", "r", "e", "o", "x" ]

使用集合来确保值列表的唯一性

¥Use a set to ensure the uniqueness of a list of values

js
const array = Array.from(document.querySelectorAll("[id]")).map((e) => e.id);

const set = new Set(array);
console.assert(set.size === array.length);

规范

Specification
ECMAScript Language Specification
# sec-set-objects

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看