JavaScript Set 介绍:全面指南 – wiki词典

JavaScript Set 介绍:全面指南

JavaScript 中的 Set 对象是一种非常有用的数据结构,它允许你存储任何类型(无论是原始值还是对象引用)的唯一值。它的设计初衷是为了解决数组中常见的重复元素问题,并提供高效的成员检测。本文将深入探讨 Set 的核心概念、主要特性、常用方法以及在实际开发中的应用场景。

1. Set 的核心概念

Set 对象是值的集合,你可以将其视为一个特殊的数组,但其中不包含任何重复的元素。这意味着如果你尝试向一个 Set 添加一个已经存在的值,它将不会被添加,且 Set 的大小也不会改变。

主要特点:

  • 唯一性: Set 中的每个值都是唯一的。
  • 无序性: Set 中的元素没有特定的顺序。你不能通过索引访问 Set 中的元素。
  • 迭代性: Set 对象是可迭代的,这意味着你可以使用 for...of 循环来遍历它的元素。
  • 类型多样性: 可以存储任何类型的值,包括 nullundefined、对象和基本数据类型。

2. 创建 Set 对象

你可以使用 Set 构造函数来创建一个新的 Set 对象。

语法:

javascript
new Set([iterable]);

iterable 是一个可选参数,如果提供,其所有元素都将被添加到新的 Set 中。这通常是一个数组。

示例:

“`javascript
// 创建一个空的 Set
const mySet1 = new Set();
console.log(mySet1); // Set(0) {}

// 从数组创建 Set,重复的值会被自动移除
const mySet2 = new Set([1, 2, 3, 2, 4, 5, 1]);
console.log(mySet2); // Set(5) {1, 2, 3, 4, 5}

// 从字符串创建 Set (字符串是可迭代的)
const mySet3 = new Set(“hello”);
console.log(mySet3); // Set(4) {‘h’, ‘e’, ‘l’, ‘o’}

// 存储不同类型的值
const mySet4 = new Set([1, ‘hello’, {a: 1}, null, undefined, 1]);
console.log(mySet4); // Set(5) {1, ‘hello’, {a: 1}, null, undefined}
“`

注意:
* NaN 被视为与 NaN 相同(尽管 NaN === NaNfalse),因此在 Set 中只能存在一个 NaN
* 对象引用被认为是不同的,即使它们的内容相同。

javascript
const obj1 = {a: 1};
const obj2 = {a: 1};
const mySet5 = new Set([obj1, obj2]);
console.log(mySet5.size); // 2 (因为 obj1 和 obj2 是不同的对象引用)

3. Set 的常用方法和属性

Set 对象提供了一系列方法来操作其元素。

属性

  • Set.prototype.size:返回 Set 对象中元素的数量。

方法

  • Set.prototype.add(value):向 Set 对象的末尾添加一个新元素。如果该元素已存在,则 Set 不会改变。返回 Set 对象本身。

    javascript
    const mySet = new Set();
    mySet.add(1); // Set(1) {1}
    mySet.add(5); // Set(2) {1, 5}
    mySet.add('text'); // Set(3) {1, 5, 'text'}
    mySet.add(1); // 1 已存在,Set 不变
    console.log(mySet.size); // 3

  • Set.prototype.delete(value):从 Set 对象中移除指定 value 的元素。如果 Set 中存在该元素并被成功移除,返回 true;否则返回 false

    javascript
    const mySet = new Set([1, 2, 3]);
    console.log(mySet.delete(2)); // true
    console.log(mySet); // Set(2) {1, 3}
    console.log(mySet.delete(5)); // false (5 不存在)

  • Set.prototype.has(value):返回一个布尔值,表示 Set 对象是否包含指定 value 的元素。

    javascript
    const mySet = new Set([1, 2, 3]);
    console.log(mySet.has(2)); // true
    console.log(mySet.has(5)); // false

  • Set.prototype.clear():移除 Set 对象中的所有元素。

    javascript
    const mySet = new Set([1, 2, 3]);
    mySet.clear();
    console.log(mySet); // Set(0) {}
    console.log(mySet.size); // 0

  • Set.prototype.forEach(callbackFn, [thisArg]):对 Set 对象中的每个元素执行一次提供的 callbackFncallbackFn 接收三个参数:valuekey (在 Set 中与 value 相同) 和 set 本身。

    javascript
    const mySet = new Set([1, 2, 3]);
    mySet.forEach((value, key, set) => {
    console.log(`Value: ${value}, Key: ${key}`);
    });
    // Output:
    // Value: 1, Key: 1
    // Value: 2, Key: 2
    // Value: 3, Key: 3

  • Set.prototype.values():返回一个 Iterator 对象,其中包含 Set 对象中的所有值。

  • Set.prototype.keys():与 values() 方法相同,因为 Set 没有键的概念。
  • Set.prototype.entries():返回一个 Iterator 对象,其中包含 Set 对象中每个元素的 [value, value] 数组。

    “`javascript
    const mySet = new Set([1, 2]);

    for (const value of mySet.values()) {
    console.log(value); // 1, 2
    }

    for (const key of mySet.keys()) {
    console.log(key); // 1, 2
    }

    for (const entry of mySet.entries()) {
    console.log(entry); // [1, 1], [2, 2]
    }
    “`

4. Set 的迭代

由于 Set 是可迭代的,你可以使用 for...of 循环直接遍历其元素,或者使用展开运算符 (...) 将 Set 转换为数组。

“`javascript
const mySet = new Set([‘apple’, ‘banana’, ‘orange’]);

// 使用 for…of 循环
for (const item of mySet) {
console.log(item);
}
// Output:
// apple
// banana
// orange

// 将 Set 转换为数组
const myArray = […mySet];
console.log(myArray); // [‘apple’, ‘banana’, ‘orange’]

// 也可以使用 Array.from()
const myArray2 = Array.from(mySet);
console.log(myArray2); // [‘apple’, ‘banana’, ‘orange’]
“`

5. Set 的常见应用场景

Set 因其唯一性和高效的成员检测,在很多场景下都非常有用。

5.1. 数组去重

这是 Set 最常见和最直接的用途。

javascript
const numbers = [1, 2, 3, 2, 4, 5, 1, 3];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3, 4, 5]

5.2. 判断元素是否存在

相比于数组的 indexOfincludes 方法(它们需要遍历整个数组),Sethas() 方法提供了更高效的 O(1)(平均时间复杂度)成员检测。

“`javascript
const bigDataSet = new Set(Array.from({length: 100000}, (_, i) => i));

console.time(‘Set.has’);
console.log(bigDataSet.has(50000)); // true
console.timeEnd(‘Set.has’); // 耗时非常短

const bigArray = Array.from({length: 100000}, (_, i) => i);
console.time(‘Array.includes’);
console.log(bigArray.includes(50000)); // true
console.timeEnd(‘Array.includes’); // 耗时相对较长
“`

5.3. 集合操作(交集、并集、差集)

Set 对象可以非常方便地进行数学上的集合操作。

“`javascript
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);

// 并集 (Union): 所有不重复的元素
const union = new Set([…setA, …setB]);
console.log(union); // Set(6) {1, 2, 3, 4, 5, 6}

// 交集 (Intersection): 两个 Set 中都存在的元素
const intersection = new Set([…setA].filter(x => setB.has(x)));
console.log(intersection); // Set(2) {3, 4}

// 差集 (Difference) A – B: 存在于 A 但不存在于 B 的元素
const difference = new Set([…setA].filter(x => !setB.has(x)));
console.log(difference); // Set(2) {1, 2}

// 差集 (Difference) B – A: 存在于 B 但不存在于 A 的元素
const differenceBMinusA = new Set([…setB].filter(x => !setA.has(x)));
console.log(differenceBMinusA); // Set(2) {5, 6}
“`

5.4. 跟踪唯一 ID 或状态

在需要跟踪页面上唯一 ID 或用户访问状态的场景中,Set 也是一个很好的选择。

“`javascript
const visitedProductIds = new Set();

function markProductAsVisited(productId) {
if (!visitedProductIds.has(productId)) {
visitedProductIds.add(productId);
console.log(Product ${productId} marked as visited for the first time.);
} else {
console.log(Product ${productId} was already visited.);
}
}

markProductAsVisited(‘prod-123’); // Product prod-123 marked as visited for the first time.
markProductAsVisited(‘prod-456’); // Product prod-456 marked as visited for the first time.
markProductAsVisited(‘prod-123’); // Product prod-123 was already visited.
“`

6. Set vs WeakSet

除了 Set,JavaScript 还提供了 WeakSetWeakSet 只能存储对象引用,并且这些引用是“弱”引用。这意味着如果对象没有其他引用指向它,它将可能被垃圾回收机制回收,即使它还在 WeakSet 中。

主要区别:

  • Set 可以存储任何类型的值,WeakSet 只能存储对象引用。
  • Set 中的引用是强引用,WeakSet 中的引用是弱引用。
  • WeakSet 不可迭代,也没有 size 属性。
  • WeakSet 只有 add, delete, has 方法。

WeakSet 主要用于内存管理,例如在不阻止对象被垃圾回收的情况下,跟踪对象的集合。

7. 总结

JavaScript Set 是一个强大而灵活的数据结构,特别适用于需要存储唯一值和进行高效成员检测的场景。无论是简单的数组去重,还是复杂的集合操作,Set 都能提供简洁高效的解决方案。理解并善用 Set 将显著提升你的 JavaScript 编程效率和代码质量。

滚动至顶部