描述
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 |
||
A.intersection(B) |
Set |
||
A.symmetricDifference(B) |
Set |
||
A.union(B) |
Set |
||
A.isDisjointFrom(B) |
Boolean |
||
A.isSubsetOf(B) |
Boolean |
||
A.isSupersetOf(B) |
Boolean |
為了使這些方法更具通用性,它們不僅接受 Set 物件,還接受任何 類似集合的物件。
類似集合的物件
所有 集合組合方法 都要求 this 是一個實際的 Set 例項,但它們的引數只需要是類似集合的。類似集合的物件 是一個提供以下功能的物件:
例如,Map 物件是類似集合的,因為它們也有 size、has() 和 keys(),因此在集合方法中使用時,它們就像鍵的集合一樣。
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 中定義:
interface GPUSupportedFeatures {
readonly setlike<DOMString>;
};
類似集合的物件要麼是隻讀的,要麼是讀寫的(請參閱上面的 IDL 中的 readonly 關鍵字)。
- 只讀的類似集合的物件具有
size屬性,以及以下方法:entries()、forEach()、has()、keys()、values()和Symbol.iterator()。 - 可寫的類似集合的物件還具有以下方法:
clear()、delete()和add()。
這些方法和屬性的行為與 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.symmetricDifference()-
接收一個集合,並返回一個包含此集合或給定集合中的元素(但不是兩者都有)的新集合。
Set.prototype.union()-
接收一個集合,並返回一個包含此集合和給定集合中的任意一個或兩個元素的新集合。
Set.prototype.values()-
返回一個新的迭代器物件,該物件將產生
Set物件中每個元素的值,按插入順序排列。 Set.prototype[Symbol.iterator]()-
返回一個新的迭代器物件,該物件將產生
Set物件中每個元素的值,按插入順序排列。
示例
使用 Set 物件
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 }
迭代集合
集合的迭代會按插入順序訪問元素。
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
實現基本集合操作
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}
與陣列的關係
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
從陣列中刪除重複元素
// 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]
與字串的關係
// 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" ]
使用集合確保值列表的唯一性
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 |
瀏覽器相容性
載入中…