描述
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 |
浏览器兼容性
加载中…