Set
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.intersection(B) |
Set |
||
A.symmetricDifference(B) |
Set |
||
A.union(B) |
Set |
||
A.isDisjointFrom(B) |
布尔值 |
||
A.isSubsetOf(B) |
布尔值 |
||
A.isSupersetOf(B) |
布尔值 |
为了使它们更具通用性,这些方法不仅接受 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]()
来生成元素。这是为了使映射成为有效的类似集合的对象,因为对于映射,迭代器生成条目,但 has()
方法接受键。
数组 不是类似集合的,因为它们没有 has()
方法或 size
属性,并且它们的 keys()
方法生成索引而不是元素。WeakSet
对象也不是类似集合的,因为它们没有 keys()
方法。
类似集合的浏览器 API
浏览器Set
类对象(或“类似集合的对象”)是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
对象中不存在具有相同值的元素,则向其中插入一个具有指定值的新元素。 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
参数,它将用作每次调用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 语言规范 # sec-set-objects |
浏览器兼容性
BCD 表格仅在浏览器中加载