Set

基线 广泛可用

此功能已得到良好确立,并且可以在许多设备和浏览器版本中使用。它自以下时间以来在所有浏览器中都可用 2015 年 7 月.

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

描述

Set 对象是值的集合。集合中的值**只能出现一次**;它在集合的集合中是唯一的。你可以按插入顺序迭代集合的元素。插入顺序对应于每个元素通过add() 方法成功插入集合的顺序(也就是说,当调用 add() 时,集合中不存在相同的元素)。

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

值相等

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

性能

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

集合组合

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

方法 返回类型 数学等价物 文氏图
A.difference(B) Set A B 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 A B 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\setminus B)\cup(B\setminus A) 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 A B 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) 布尔值 A B = 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) 布尔值 A B 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) 布尔值 A B A\supseteq B A Venn diagram with two circles. A is a superset of B because B is completely contained in A.

为了使它们更具通用性,这些方法不仅接受 Set 对象,还接受任何类似集合的对象。

类似集合的对象

所有集合组合方法都要求this 为实际的 Set 实例,但它们的参数只需要是类似集合的。类似集合的对象是提供以下内容的对象

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

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

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() 方法而不是[Symbol.iterator]() 来生成元素。这是为了使映射成为有效的类似集合的对象,因为对于映射,迭代器生成条目,但 has() 方法接受

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

类似集合的浏览器 API

浏览器Set 类对象(或“类似集合的对象”)是Web API 接口,其行为在许多方面类似于 Set

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

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

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

类似集合的对象是只读的或读写的(参见上面 IDL 中的 readonly 关键字)。

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

以下是只读类似集合的浏览器对象的示例

以下是可写类似集合的浏览器对象的示例

构造函数

Set()

创建一个新的 Set 对象。

静态属性

Set[Symbol.species]

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

实例属性

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

Set.prototype.constructor

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

Set.prototype.size

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

Set.prototype[Symbol.toStringTag]

[Symbol.toStringTag] 属性的初始值为字符串 "Set"。此属性用于Object.prototype.toString()

实例方法

Set.prototype.add()

如果 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 参数,它将用作每次调用 callbackFnthis 值。

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[Symbol.iterator]()

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

示例

使用 Set 对象

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 }

迭代集合

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

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

实现基本集合操作

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}

与数组的关系

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

从数组中移除重复元素

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]

与字符串的关系

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" ]

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

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

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

规范

规范
ECMAScript 语言规范
# sec-set-objects

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅