Set
Baseline Widely available *
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
* Some parts of this feature may have varying levels of support.
Set
对象允许你存储任何类型(无论是原始值还是对象引用)的唯一值。
描述
Set
对象是值的合集(collection)。集合(set)中的元素只会出现一次,即集合中的元素是唯一的。你可以按照插入顺序迭代集合中的元素。插入顺序对应于 add()
方法成功将每一个元素插入到集合中(即,调用 add()
方法时集合中不存在相同的元素)的顺序。
规范要求集合的实现是“对合集中的元素的平均访问时间与集合中元素的数量呈次线性关系”。因此,它可以在内部表示为散列表(查找的时间复杂度为 O(1))、搜索树(查找的时间复杂度为 O(log(N)))或任何其他的时间复杂度低于 O(N) 的数据结构。
值的相等
性能
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, "一"], [2, "二"], [4, "四"], ]); console.log(a.union(b)); // Set(4) {1, 2, 3, 4}
备注: 类集合协议调用 keys()
方法而不是 [Symbol.iterator]()
来生成元素。这是为了使 Map 成为有效的类集合对象,因为对于 Map,迭代器生成条目,而 has()
方法接受键。
数组不是类集合对象,因为它们没有 has()
方法或 size
属性,并且它们的 keys()
方法生成索引而不是元素。WeakSet
对象也不是类集合对象,因为它们没有 keys()
方法。
类 Set 浏览器 API
浏览器类 Set
对象(或“类集合对象”)是行为与 Set
类似的 Web API 接口。
就像 Set
一样,元素可以按照它们被添加到对象的顺序进行迭代。类 Set
对象也有与 Set
相同名称和行为的属性及方法。但是,与 Set
不同的是,前者只允许每个条目特定的预定义类型。
允许的类型由规范的 IDL 定义给出。例如,GPUSupportedFeatures
是一个类 Set
对象,它必须以字符串为键/值。规范的 IDL 对其的定义如下:
interface GPUSupportedFeatures { readonly setlike<DOMString>; };
类 Set
对象是只读或可读写的(参见上面的 IDL 中的 readonly
关键字)。
- 只读的类
Set
对象具有size
属性及以下方法:entries()
、forEach()
、has()
、keys()
、values()
和[Symbol.iterator]()
。 - 可写的类
Set
对象此外还具有以下方法:clear()
、delete()
和add()
。
除了对条目类型的限制外,方法和属性的行为与 Set
中的等效成员相同。
以下是只读的类 Set
浏览器对象的示例:
以下是可写的类 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
对象中插入一个新的具有指定值的元素。Set.prototype.clear()
从
Set
对象中移除所有元素。Set.prototype.delete()
移除与
value
关联的元素,并返回一个布尔值来表示是否移除成功。Set.prototype.has(value)
会在此之后返回false
。Set.prototype.difference()
接受一个集合并返回一个新集合,该集合包含此集合中但不在给定集合中的元素。
Set.prototype.entries()
返回一个新的迭代器对象,该对象包含
Set
对象中的代表每个元素的[value, value]
数组。这与Map
对象类似,因此Set
的每个条目的 key 和 value 都相同。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 是不同对象的引用,所以这是可以的 mySet1.has(1); // true mySet1.has(3); // false,因为并未将 3 添加到集合中 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); // 从集合中移除 5 mySet1.has(5); // false,5 已从集合中移除 mySet1.size; // 4,因为我们刚刚移除了一个值 mySet1.add(5); // Set(5) { 1, 'some text', {...}, {...}, 5 }——先前删除的元素会作为新的元素被添加,不会保留删除前的原始位置 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 // 键和值是相同的 for (const [key, value] of mySet1.entries()) { console.log(key); } // 1、"some text"、{ "a": 1, "b": 2 }、{ "a": 1, "b": 2 }、5 // 使用 Array.from 将 Set 对象转换为数组对象 const myArr = Array.from(mySet1); // [1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}, 5] // 如果在 HTML 文档中使用,也可以: mySet1.add(document.body); mySet1.has(document.querySelector("body")); // true // 在 Set 和 Array 之间转换 const mySet2 = new Set([1, 2, 3, 4]); console.log(mySet2.size); // 4 console.log([...mySet2]); // [1, 2, 3, 4] // 可以通过如下代码模拟求交集 const intersection = new Set([...mySet1].filter((x) => mySet2.has(x))); // 可以通过如下代码模拟求差集 const difference = new Set([...mySet1].filter((x) => !mySet2.has(x))); // 使用 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; } // 示例 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); // 返回 true union(setA, setC); // 返回 Set {1, 2, 3, 4, 5, 6} intersection(setA, setC); // 返回 Set {3, 4} symmetricDifference(setA, setC); // 返回 Set {1, 2, 5, 6} difference(setA, setC); // 返回 Set {1, 2}
与数组的关系
const myArray = ["value1", "value2", "value3"]; // 使用常规的 Set 构造函数将 Array 转换为 Set const mySet = new Set(myArray); mySet.has("value1"); // 返回 true // 使用展开语法将 Set 转换为 Array。 console.log([...mySet]); // 将显示与 myArray 完全相同的数组
数组去重
// 用于从数组中删除重复元素 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]
与字符串的关系
// 大小写敏感(包含 "F" 和 "f") new Set("Firefox"); // Set(7) [ "F", "i", "r", "e", "f", "o", "x" ] // 忽略重复项("f" 出现了两次,但是只包含一个) 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);
规范
Specification |
---|
ECMAScript® 2026 Language Specification # sec-set-objects |