
数据的“洁癖”管家深入解析 JavaScript Set 为什么我们需要 Set在开发中我们经常遇到这样的场景数组去重从后端获取了一堆标签需要去除重复项。快速查找判断某个用户 ID 是否已经在黑名单中。集合运算求两个用户群体的交集、并集或差集。如果使用传统的Array这些操作往往需要嵌套循环或复杂的逻辑代码冗长且性能低下尤其是数据量大时。而Set天生就是为了解决“唯一性”和“高效查找”而生的。通俗比喻Array数组像是一个普通的储物箱。你可以往里面扔任何东西哪怕是一模一样的苹果它也会照单全收。如果你想找某个苹果你得把箱子倒出来一个个看。Set集合像是一个带有自动识别功能的智能货架。唯一性如果你试图放入一个已经存在的苹果货架会直接拒绝“这个已经有了”高效性货架内部有索引问你“有没有红富士”它能瞬间回答“有”或“没有”不需要逐个翻找。 目录 核心概念与基本用法️ 常用 API 详解⚔️ 实战场景去重与集合运算 进阶技巧WeakSet 与性能分析❌ 常见误区与坑点 总结与选型建议1. 核心概念与基本用法Set本身是一个构造函数用来生成Set数据结构。✅ 基本创建// 1. 创建一个空的 Setconsts1newSet();// 2. 通过数组初始化自动去重consts2newSet([1,2,3,2,1]);console.log(s2);// Set(3) { 1, 2, 3 }// 3. 通过字符串初始化字符串也是可迭代对象consts3newSet(hello);console.log(s3);// Set(4) { h, e, l, o } - 注意 l 只保留了一个⚠️ 判断相等的规则Set内部判断两个值是否相等使用的是“Same-value-zero”算法。它类似于严格相等运算符 ()。主要区别在Set中NaN等于NaN。constsnewSet();s.add(NaN);s.add(NaN);console.log(s.size);// 1因为 NaN NaN 在 Set 中被视为相同s.add({});s.add({});console.log(s.size);// 3因为两个空对象引用地址不同被视为不同元素2. ️ 常用 API 详解Set的实例方法非常直观主要分为增、删、查、清四类。方法描述返回值add(value)添加某个值返回 Set 结构本身支持链式调用Set对象delete(value)删除某个值返回一个布尔值表示删除是否成功booleanhas(value)判断某个值是否存在于 Set 中booleanclear()清除所有成员没有返回值undefinedsize属性返回成员总数number 代码示例constmySetnewSet();// 添加元素mySet.add(1).add(2).add(2);// 链式调用第二个 2 被忽略console.log(mySet.size);// 2// 判断存在console.log(mySet.has(1));// trueconsole.log(mySet.has(3));// false// 删除元素mySet.delete(1);console.log(mySet.has(1));// false// 清空mySet.clear();console.log(mySet.size);// 0 遍历方法Set默认是可迭代的可以使用for...of循环也提供了以下遍历方法keys(): 返回键名的遍历器对于 Set键名即值values(): 返回键值的遍历器entries(): 返回键值对的遍历器forEach(): 使用回调函数遍历每个成员constsetnewSet([red,green,blue]);for(letitemofset){console.log(item);}// red// green// blue// 转换为数组进行高阶操作constarr[...set];// 或者constarr2Array.from(set);3. ⚔️ 实战场景去重与集合运算这是Set最发光发热的地方。✅ 场景一数组去重最经典用法以前我们需要用indexOf或filter配合对象哈希来去重现在一行代码搞定。constarr[1,2,2,3,4,4,5];// 方法Set - ArrayconstuniqueArr[...newSet(arr)];// 或者constuniqueArr2Array.from(newSet(arr));console.log(uniqueArr);// [1, 2, 3, 4, 5]注意这种方法只能去除基本类型的重复值。如果数组中包含对象{}由于引用地址不同无法去重。✅ 场景二集合运算交、并、差利用Set和数组方法可以轻松实现数学中的集合运算。constanewSet([1,2,3]);constbnewSet([2,3,4]);// 1. 并集 (Union): a bconstunionnewSet([...a,...b]);console.log(union);// Set(4) { 1, 2, 3, 4 }// 2. 交集 (Intersection): a ∩ b (既在 a 又在 b)constintersectionnewSet([...a].filter((x)b.has(x)));console.log(intersection);// Set(2) { 2, 3 }// 3. 差集 (Difference): a - b (在 a 但不在 b)constdifferencenewSet([...a].filter((x)!b.has(x)));console.log(difference);// Set(1) { 1 }✅ 场景三高性能查找当数据量很大时Array.includes()的时间复杂度是O(N)而Set.has()的时间复杂度接近O(1)。constlargeArrayArray.from({length:100000},(_,i)i);constlargeSetnewSet(largeArray);console.time(Array Search);largeArray.includes(99999);console.timeEnd(Array Search);// 较慢console.time(Set Search);largeSet.has(99999);console.timeEnd(Set Search);// 极快4. 进阶技巧WeakSet 与性能分析 WeakSet垃圾回收的好帮手WeakSet结构与Set类似也是不重复值的集合。但它有两个显著区别成员只能是对象不能是基本类型值。弱引用其中的对象如果没有其他引用会被垃圾回收机制自动回收不会导致内存泄漏。适用场景存储 DOM 节点标记、缓存对象引用等临时性数据。constwsnewWeakSet();constobj{};ws.add(obj);console.log(ws.has(obj));// trueobjnull;// 解除引用// 此时ws 中的 obj 可能会被垃圾回收具体取决于引擎实现注意WeakSet不可遍历没有size属性也没有keys/values/entries方法。 性能对比总结操作ArraySet优势方查找元素O(N) (线性扫描)O(1) (哈希表)Set(数据越大优势越明显)添加元素O(1) (末尾)O(1)平手删除元素O(N) (需查找移动)O(1)Set去重需额外逻辑天然支持Set内存占用较低略高 (哈希表开销)Array5. ❌ 常见误区与坑点1. 引用类型无法自动去重constsnewSet();s.add({id:1});s.add({id:1});console.log(s.size);// 2因为两个对象内存地址不同解决方案如果需要基于对象的某个属性去重需先处理数据如转为 JSON 字符串或使用 Map。2. 0 和 -0在Set中0和-0被视为相同的值。constsnewSet();s.add(0);s.add(-0);console.log(s.size);// 13. Set 转数组的顺序Set保持插入顺序。遍历时先添加的元素先被遍历。这一点与某些语言中的 HashSet无序不同请务必记住。6. 总结与选型建议 核心总结特性ArraySet有序性✅ 有序✅ 有序 (插入顺序)唯一性❌ 可重复✅ 唯一查找速度慢 (O(N))快 (O(1))数据类型任意任意 (WeakSet 仅限对象)主要用途列表、栈、队列去重、快速查找、集合运算 博主寄语日常开发遇到“去重”需求无脑选Set。[...new Set(arr)]是最优雅的写法。性能敏感如果需要频繁判断“某元素是否存在”请将数据存入Set而不是Array。DOM 操作如果需要标记一组 DOM 元素且不担心内存泄漏问题可以考虑WeakSet。面试加分项提到Set基于哈希表实现所以查找效率高提到NaN的特殊处理提到WeakSet的垃圾回收机制。记住口诀数组去重用 Set一行代码解千愁。查找频繁建集合哈希原理速度快。对象引用要注意地址不同算两个。弱引用里存节点内存泄漏不用忧。交集并集差集算Filter Has 组合秀。希望这篇文档能帮你彻底掌握Set的用法如果有疑问欢迎在评论区留言。喜欢这篇文章吗记得点赞、收藏、转发哦❤️