Set

Baseline 廣泛可用 *

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2015 年 7 月⁩以來,各瀏覽器均已提供此特性。

* 此特性的某些部分可能存在不同級別的支援。

Set 物件允許您儲存任何型別的唯一值,無論是 原始值還是物件引用。

描述

Set 物件是值的集合。集合中的值只能出現一次;它在集合的集合中是唯一的。您可以按照插入順序迭代集合中的元素。插入順序對應於使用 add() 方法成功將每個元素插入到集合中的順序(即,當呼叫 add() 時,集合中已不存在相同值的元素)。

規範要求集合的實現“平均而言,提供訪問時間是集合元素數量的亞線性”。因此,它可以在內部表示為雜湊表(O(1) 查詢)、搜尋樹(O(log(N)) 查詢)或任何其他資料結構,只要其複雜度優於 O(N)。

值相等性

值相等性基於 SameValueZero 演算法。(它曾經使用 SameValue,該演算法將 0-0 視為不同。請檢查 瀏覽器相容性。)這意味著 NaN 被視為與 NaN 相同(即使 NaN !== NaN),並且根據 === 運算子的語義,所有其他值都被視為相等。此外,對於物件鍵,相等性基於物件標識。它們透過引用進行比較,而不是透過值。有關示例,請參閱 使用 Set 物件

效能

has 方法使用一種方法來檢查一個值是否存在於集合中,該方法平均而言比測試已新增到集合中的大多數元素更快。特別是,當陣列的 length 等於集合的 size 時,它平均而言比 Array.prototype.includes 方法更快。

集合組合

Set 物件提供了一些方法,允許您像進行數學運算一樣組合集合。這些方法包括:

方法 返回型別 數學等價 韋恩圖
A.difference(B) Set ABA\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 ABA\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 (AB)(BA)(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 ABA\cup B A Venn diagram where two circles overlap. The union of A and B is the region contained by either or both circles.
A.isDisjointFrom(B) Boolean AB=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 ABA\subseteq B A Venn diagram with two circles. A is a subset of B because A is completely contained in B.
A.isSupersetOf(B) Boolean ABA\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]() 來生成元素。這是為了使 Map 成為有效的類似集合的物件,因為對於 Map 而言,迭代器生成的是條目,而 has() 方法接受

陣列 不是類似集合的,因為它們沒有 has() 方法或 size 屬性,並且它們的 keys() 方法生成的是索引而不是元素。 WeakSet 物件也不是類似集合的,因為它們沒有 keys() 方法。

類似集合的瀏覽器 API

瀏覽器中的 Set-like 物件(或“類似集合的物件”)是 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.prototype.clear()

Set 物件中移除所有元素。

Set.prototype.delete()

如果指定值在此集合中,則將其從此集合中移除。

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[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® 2026 語言規範
# sec-set-objects

瀏覽器相容性

另見