es set

es set
 
 
 
 
在 JavaScript (ES6) 中,Set 是一种全新的集合数据结构。它类似于数组(Array),但核心特征是:它的成员的值都是唯一的,没有重复的值。
在现代开发中,Set 最常用于数组去重、求交集/并集以及高效的成员包含性检查。

🛠️ 1. Set 的基础增删改查 API
Set 的操作非常直观,它没有索引索引值(不能通过 [0] 访问),而是使用专属的一套方法:
javascript
// 1. 初始化(可以传入一个数组作为初始数据)
const mySet = new Set([1, 2, 3, 3, 4]); 
console.log(mySet); // 输出: Set(4) { 1, 2, 3, 4 } —— 💡 重复的 3 自动被过滤了

// 2. 增:.add(value) —— 可以链式调用
mySet.add(5).add(6);

// 3. 判:.has(value) 检查某个值是否存在(性能极高,远快于数组的 includes)
console.log(mySet.has(3)); // 输出: true
console.log(mySet.has(9)); // 输出: false

// 4. 量:.size 获取集合中的元素总数(相当于数组的 length)
console.log(mySet.size);   // 输出: 6

// 5. 删:.delete(value) 移除某个元素
mySet.delete(2);

// 6. 清:.clear() 清空所有数据
// mySet.clear();
请谨慎使用此类代码。

🔄 2. Set 的遍历方式
Set 原生保持元素的插入顺序,可以完美使用 for...offorEach 进行遍历:
javascript
const mySet = new Set(['苹果', '香蕉', '橘子']);

// 💡 方式 A:直接使用 for...of 遍历
for (const item of mySet) {
  console.log(item);
}

// 💡 方式 B:使用 forEach
mySet.forEach((value) => {
  console.log(value);
});
请谨慎使用此类代码。

🚀 3. 实战王牌场景
场景一:最优雅的“数组去重” ✨
结合 扩展运算符(...),可以一行代码搞定常规数组去重:
javascript
const originalArray =;

# 一行代码完成去重并重回数组
const uniqueArray = [...new Set(originalArray)];

console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]
请谨慎使用此类代码。
场景二:求两集合的“交集”与“差集”
javascript
const setA = new Set([1, 2, 3]);
const setB = new Set([2, 3, 4]);

// 1. 求交集 (Intersection) -> [2, 3]
const intersection = new Set([...setA].filter(x => setB.has(x)));

// 2. 求差集 (Difference) -> [1] (在 A 中但不在 B 中)
const difference = new Set([...setA].filter(x => !setB.has(x)));
请谨慎使用此类代码。

🥊 4. 核心对比:Set vs Array (数组)
特性数组 Array集合 Set
重复值 允许元素完全重复 绝对不允许任何重复值
查找性能 arr.includes(x)O(N),数据量大时慢 set.has(x)O(1),数据量巨大时依然极快
元素访问 可以通过索引访问 arr[0] 不能通过索引访问,只能遍历或转化为数组
应用场景 用于需要排序、有索引的普通队列或列表 用于去重、快速查找、唯一性校验的独立数据块

💡 避坑注意:对象的唯一性
Set 判断两个值是否相同,使用的是类似于严格相等(===)的算法(针对 NaN 有特殊处理,Set 认为 NaN 等于自身)。
需要特别注意:两个空对象 {} 在 JavaScript 里是不相等的,所以它们可以同时存在于 Set 中。
javascript
const set = new Set();
set.add({});
set.add({});
console.log(set.size); // 输出: 2 (因为两个对象的内存地址不同,Set 认为它们是两个独立的值)
请谨慎使用此类代码。
既然你在接触 Set,同时之前深入研究了 MapWeakMap,需要我为你接着引申介绍它的弱引用版本 WeakSet 的应用场景(如防止方法被外部滥用调用),还是看看它如何与你现有的 前端数据管理逻辑 结合?
如果你想深入,告诉我:
  • 你目前是在编写 纯前端(React / UI组件) 的去重和过滤逻辑?
  • 还是在写 Node.js (Koa) 服务端的内存状态管理?
    我好为你匹配对应场景的进阶范例。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
weakset
 
 
 
 
在 JavaScript (ES6) 中,WeakSetSet 的弱引用兄弟版本。和 WeakMap 的原理类似,它诞生的核心目的就是为了在特定的高级场景下防止内存泄漏。
简单来说:WeakSet 内部只能存放对象(Object),并且它对这些对象的引用是“弱引用”。一旦外界没有其他变量指向这个对象,垃圾回收机制(GC)就会在某个时刻把它连带从 WeakSet 里一起自动清除销毁。

🥊 1. 核心区别:WeakSet vs Set
特性SetWeakSet
存放的内容 任意类型的值(数字、字符串、对象等) 只能是对象(Object)(或特定环境下的非注册 Symbol)
引用方式 强引用(阻碍垃圾回收) 弱引用(不阻碍垃圾回收,对象在外界销毁时,内部会自动消失)
遍历与大小 可遍历,有 .size 属性,支持 forEach 绝对不可遍历,无 .size 属性,没有任何遍历方法

🛠️ 2. WeakSet 的基础 API
由于内部的对象随时可能被垃圾回收车悄悄“收走”,WeakSet 同样不具备任何获取大小或清空的方法(没有 .size,没有 .clear())。它只有 3 个基本方法:
javascript
// 1. 初始化
const ws = new WeakSet();

let obj1 = { name: "马良" };
let obj2 = { age: 18 };

// 2. 添:.add() —— 💡 必须传入对象!
ws.add(obj1);
ws.add(obj2);

// ws.add(123); // ❌ 报错!TypeError: Invalid value used in weak set

// 3. 判:.has()
console.log(ws.has(obj1)); // 输出: true

// 4. 删:.delete()
ws.delete(obj1);
请谨慎使用此类代码。

🚀 3. 经典应用场景
在日常前端开发或编写 Node.js (Koa) 中间件时,WeakSet 最常见且最重要的舞台是:给特定对象打上“只读标签”或“状态标记”,同时完美避免内存泄漏。
场景 A:给类的方法做安全鉴权(防止方法被外部非法调用)
如果你写了一个类(或者多层结构的 Service/组件),某个方法你只希望通过实例本身被触发,不希望被外界通过 method.call(obj) 偷梁换柱地滥用,可以用 WeakSet 储存合法实例。
javascript
const foos = new WeakSet(); // 存储所有合法实例

class Foo {
  constructor() {
    foos.add(this); // 初始化时,把当前合法实例加进弱引用集合
  }
  
  method() {
    // 💡 检查调用者(this)是否在合法的集合里
    if (!foos.has(this)) {
      throw new TypeError("Foo.prototype.method 只能在合法的 Foo 实例上调用!");
    }
    console.log("方法安全执行...");
  }
}

const f = new Foo();
f.method(); // ✅ 正常运行

// ❌ 尝试用一个普通对象去伪造并调用该方法
const fake = {};
Foo.prototype.method.call(fake); // 💥 直接抛出错误!
请谨慎使用此类代码。
好处:当实例 f 被销毁(比如用户路由切走,或者内存变量释放时),foos 里的弱引用标记会自动人间蒸发,绝不会干扰原实例的垃圾回收机制。
场景 B:防止函数的“递归死循环”(追踪正在处理的对象)
当你在解析一个极其庞大的深层嵌套对象,或者在前端进行 DOM 树递归查找、做深拷贝时,如果对象内部存在循环引用(比如 A.parent = BB.child = A),递归就会死循环直到堆栈溢出。此时可以用 WeakSet 记录处理状态。
javascript
const processedObjects = new WeakSet();

function handleData(obj) {
  // 💡 如果对象已经被记录正在处理了,说明碰到了循环引用,直接跳过
  if (processedObjects.has(obj)) {
    return; 
  }

  // 标记当前对象已处理过
  processedObjects.add(obj);

  // 递归处理子属性...
  for (let key in obj) {
    if (typeof obj[key] === 'object' && obj[key] !== null) {
      handleData(obj[key]);
    }
  }
}
请谨慎使用此类代码。

至此,我们已经把 ES6 引入的四大核心新集合结构 MapWeakMapSetWeakSet 的底层机理全部对齐了。
如果你的项目需要更工程化的落地,接下来我们是:
  • 探讨如何在 前端复杂 UI 状态管理 / 虚拟 DOM 依赖追踪 中结合这些结构来写工具类?
  • 还是你准备回到之前的 FastAPI / Flask 或者是 Node.js 的业务开发里,需要我为你梳理其他核心机制?
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

posted on 2026-09-21 14:08  漫思  阅读(6)  评论(0)    收藏  举报

导航