在 JavaScript 的世界里,Set 是一种非常纯粹的数据结构。如果说 Array(数组)是一个有顺序、允许重复的“购物清单”,那么 Set(集合)更像是一个具有排他性的“俱乐部”:它只在乎你“在不在”,而不在乎你在哪个位置,且绝不允许同一个成员出现两次。
核心特性:唯一性与查找效率
Set 最根本的逻辑在于值的唯一性。无论你尝试向其中添加多少次相同的值,它只会保留一个。
这种特性的底层优势在于查找效率。在数组中寻找一个元素,程序通常需要从头到尾“扫描”一遍(复杂度为 );而在 Set 中,它通过类似哈希表的机制,几乎可以在瞬间定位元素是否存在(复杂度趋近于 )。
基本操作
JavaScript
// 创建一个 Set
const tags = new Set(['JavaScript', 'Node.js', 'JavaScript']);
// 常用方法
tags.add('TypeScript'); // 添加元素
tags.has('Node.js'); // 检查是否存在 -> true
tags.delete('JavaScript'); // 删除元素
console.log(tags.size); // 获取长度 -> 2 (因为重复的被自动过滤了)
// 遍历
tags.forEach(value => console.log(value));
Set vs Array:该选哪一个?
很多开发者习惯于“一刀切”地使用数组,但在处理大量数据时,选择正确的数据结构能带来质的性能提升。
| 特性 | Array (数组) | Set (集合) |
|---|---|---|
| 重复性 | 允许重复 | 严禁重复 |
| 顺序性 | 严格按索引排序 | 记录插入顺序 |
| 查找效率 | 慢 () | 极快 () |
| 主要用途 | 存储有序列表、频繁随机访问 | 去重、成员资格检查 |
进阶:数学意义上的集合运算
在处理复杂逻辑时,我们经常需要对比两组数据的关系。虽然 JavaScript 的 Set 原生方法在 2024 年之前比较简陋,但现在的标准已经(或正在)引入原生的集合操作方法。
-
交集 (Intersection):获取两个集合中共同拥有的部分。
-
并集 (Union):合并两个集合并自动去重。
-
差集 (Difference):存在于 A 但不存在于 B 的部分。
代码实现示例:
JavaScript
const a = new Set([1, 2, 3]);
const b = new Set([3, 4, 5]);
// 并集
const union = new Set([...a, ...b]); // [1, 2, 3, 4, 5]
// 交集
const intersection = new Set([...a].filter(x => b.has(x))); // [3]
必须注意的“坑”
-
对象的唯一性:
Set判断“相同”是基于引用的。两个内容一模一样的对象{}和{}在Set看来是不同的,因为它们指向内存中不同的地址。 -
NaN 的特殊处理:在严格相等
===中,NaN !== NaN,但在Set内部,它认为所有的NaN都是同一个值。 -
无法随机访问:你不能通过
set[0]去拿第一个值,必须通过迭代器或转回数组。
实用小技巧:一行代码去重
这是 Set 最被广泛使用的场景,简洁且高效:
JavaScript
const numbers = [1, 2, 2, 3, 4, 4, 5];
const uniqueNumbers = [...new Set(numbers)];
// 结果: [1, 2, 3, 4, 5]
如果你正在处理涉及大量权限校验、标签系统或者需要处理海量唯一标识符的业务,Set 会是比 Array 优雅得多的选择。