JS — ES6 知识点笔记: Set

Set 的基本用法

Set 是 ES6 引入的一种新的数据结构,它类似于数组,但一个关键区别:Set 中的值都是唯一的,没有重复的值。

Set本身是一个构造函数,用来生成 Set 数据结构。

重点:唯一的!唯一的!唯一的!

JS
// 创建 Set
const mySet = new Set();

// 添加值
mySet.add(1);
mySet.add(2);
mySet.add(2); // 这个不会被添加,因为 2 已经存在
mySet.add('hello');
mySet.add({ name: 'John' });

console.log(mySet.size); // 4
console.log(mySet.has(2)); // true
点击展开查看更多

Set 的基本特性

1. 值的唯一性

JS
const numbers = [1, 2, 2, 3, 4, 4, 5]; // 2 和 4 重复
const uniqueNumbers = new Set(numbers);

console.log(uniqueNumbers); // Set(5) {1, 2, 3, 4, 5}
console.log([...uniqueNumbers]); // [1, 2, 3, 4, 5] - 转回数组
点击展开查看更多

2. 值的相等性判断

Set 使用 “SameValueZero” 算法判断值是否相等,类似于 ===,但有一些区别

JS
const set = new Set();

set.add(1);
set.add('1'); // 可以添加,因为 1 和 '1' 不同类型
set.add(NaN);
set.add(NaN); // NaN 只能存在一个,虽然 NaN !== NaN

console.log(set); // Set(3) {1, '1', NaN}
点击展开查看更多

3. 对象引用的处理(去重)

JS
const set = new Set();

const obj1 = { name: 'Alice' };
const obj2 = { name: 'Alice' }; // 不同的对象引用
const obj3 = obj1; // 相同的对象引用

set.add(obj1);
set.add(obj2); // 会被添加,因为 obj1 !== obj2
set.add(obj3); // 不会被添加,因为 obj1 === obj3

console.log(set.size); // 2
点击展开查看更多

Set 的常用方法

1. 基本操作

JS
// 定义
const set = new Set();

// 添加元素
set.add('apple');
set.add('banana');
set.add('orange');

// 检查是否存在
console.log(set.has('apple')); // true
console.log(set.has('grape')); // false

// 删除元素
set.delete('banana');

// 清空 Set
// set.clear();

// 获取大小
console.log(set.size); // 2
点击展开查看更多

2. 遍历 Set

Set 结构的实例有四个遍历方法,可以用于遍历成员。

  1. Set.prototype.keys():返回 键名 的遍历器
  2. Set.prototype.values():返回 键值 的遍历器
  3. Set.prototype.entries():返回 键值对 的遍历器
  4. Set.prototype.forEach():使用回调函数遍历每个成员
JS
const fruitSet = new Set(['apple', 'banana', 'orange', 'grape']);

// 使用 for...of
for (let fruit of fruitSet) {
  console.log(fruit);
}

// 使用 forEach
fruitSet.forEach((value, valueAgain) => {
  console.log(value); // Set 的 forEach 回调中,value 和 key 相同
});

// 获取值的迭代器
const values = fruitSet.values();
console.log(values.next().value); // 'apple'

// 获取键的迭代器(Set 中键和值相同)
const keys = fruitSet.keys();
console.log([...keys]); // ['apple', 'banana', 'orange', 'grape']

// 获取键值对迭代器
const entries = fruitSet.entries();
for (let [key, value] of entries) {
  console.log(`${key} = ${value}`); // key 和 value 相同
}
点击展开查看更多

3. 初始化 Set

JS
// 从数组初始化
const set1 = new Set([1, 2, 3, 4, 4, 5]); // 4 重复
console.log(set1); // Set(5) {1, 2, 3, 4, 5}

// 从字符串初始化(会拆分为字符)
const set2 = new Set('hello');
console.log(set2); // Set(4) {'h', 'e', 'l', 'o'}

// 从可迭代对象初始化
function* numberGenerator() {
  yield 1;
  yield 2;
  yield 2;
  yield 3;
}
const set3 = new Set(numberGenerator());
console.log(set3); // Set(3) {1, 2, 3}
点击展开查看更多

实际应用

1. 数组去重

JS
// 简单数组去重
const numbers = [1, 2, 2, 3, 4, 4, 5, 5];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3, 4, 5]

// 对象数组去重(基于特定属性)
const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 1, name: 'Alice' }, // 重复
  { id: 3, name: 'Charlie' }
];

const uniqueUsers = Array.from(
  new Map(users.map(user => [user.id, user])).values()
);
console.log(uniqueUsers); // 去重后的用户数组
点击展开查看更多

2. 集合运算

这其实不是 ES6 时提出的,是 ES2025 为 Set 结构添加了以下集合运算方法。

  1. Set.prototype.intersection(other):交集
  2. Set.prototype.union(other):并集
  3. Set.prototype.difference(other):差集
  4. Set.prototype.symmetricDifference(other):对称差集
  5. Set.prototype.isSubsetOf(other):判断是否为子集
  6. Set.prototype.isSupersetOf(other):判断是否为超集
  7. Set.prototype.isDisjointFrom(other):判断是否不相交
JS
class SetOperations {
  // 并集
  static union(setA, setB) {
    return new Set([...setA, ...setB]);
  }
  
  // 交集
  static intersection(setA, setB) {
    return new Set([...setA].filter(x => setB.has(x)));
  }
  
  // 差集 (A - B)
  static difference(setA, setB) {
    return new Set([...setA].filter(x => !setB.has(x)));
  }
  
  // 对称差集 (A ∪ B - A ∩ B)
  static symmetricDifference(setA, setB) {
    return new Set([
      ...this.difference(setA, setB),
      ...this.difference(setB, setA)
    ]);
  }
  
  // 子集判断
  static isSubset(setA, setB) {
    return [...setA].every(x => setB.has(x));
  }
}

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

console.log(SetOperations.union(setA, setB)); // Set(6) {1, 2, 3, 4, 5, 6}
console.log(SetOperations.intersection(setA, setB)); // Set(2) {3, 4}
console.log(SetOperations.difference(setA, setB)); // Set(2) {1, 2}
点击展开查看更多

3. 标签系统

JS
class TagManager {
  constructor() {
    this.tags = new Set();
  }
  
  addTag(tag) {
    const normalizedTag = tag.toLowerCase().trim();
    if (normalizedTag) {
      this.tags.add(normalizedTag);
    }
  }
  
  removeTag(tag) {
    this.tags.delete(tag.toLowerCase());
  }
  
  hasTag(tag) {
    return this.tags.has(tag.toLowerCase());
  }
  
  getTags() {
    return [...this.tags];
  }
  
  // 合并标签
  mergeTags(otherTags) {
    otherTags.forEach(tag => this.addTag(tag));
  }
}

const manager = new TagManager();
manager.addTag('JavaScript');
manager.addTag(' programming ');
manager.addTag('javascript'); // 不会重复添加
console.log(manager.getTags()); // ['javascript', 'programming']
点击展开查看更多

4. 访问控制

JS
class AccessControl {
  constructor() {
    this.allowedUsers = new Set();
    this.blockedUsers = new Set();
  }
  
  grantAccess(userId) {
    this.allowedUsers.add(userId);
    this.blockedUsers.delete(userId); // 如果之前被阻止,现在取消阻止
  }
  
  revokeAccess(userId) {
    this.allowedUsers.delete(userId);
  }
  
  blockUser(userId) {
    this.blockedUsers.add(userId);
    this.allowedUsers.delete(userId);
  }
  
  canAccess(userId) {
    return this.allowedUsers.has(userId) && !this.blockedUsers.has(userId);
  }
  
  // 批量操作
  grantAccessToUsers(userIds) {
    userIds.forEach(id => this.grantAccess(id));
  }
}
点击展开查看更多

版权声明

作者: Donghai

链接: https://mgrowup.com/posts/js/es6-set/

许可证: CC BY-NC-SA 4.0

文章已根据知识共享署名-非商业性使用-相同方式共享4.0国际许可协议授权。请注明来源,仅非商业使用,并保持相同的许可协议。

评论

开始搜索

输入关键词搜索文章内容

↑↓
ESC
⌘K 快捷键