你以为求交集只能手写循环,今天 Set 把七种集合运算做进了语言层
写了三年 JavaScript,给两个 Set 求交集这件事,你大概率写过这样的代码:
// 手写交集
function intersection(a, b) {
const result = new Set();
for (const item of b) {
if (a.has(item)) result.add(item);
}
return result;
}
const activePermissions = new Set(['read', 'write', 'delete']);
const adminPermissions = new Set(['write', 'admin', 'delete']);
intersection(activePermissions, adminPermissions); // Set { 'write', 'delete' }
每次用到都要自己写一个函数,不同项目里实现还略有差异——有的用 filter,有的用 reduce,有的直接 for 循环。2024 年 6 月之后,这件事不用再手写了。
七种集合运算,现在语言自带
ES2024 给 Set.prototype 加了七种方法,四种返回新 Set(union、intersection、difference、symmetricDifference),三种返回布尔值(isSubsetOf、isSupersetOf、isDisjointFrom)。全部 Immutable,原来的 Set 不会被修改。
const tags = new Set(['css', 'javascript', 'webgpu', 'typescript']);
const popularTags = new Set(['javascript', 'webgpu', 'ai']);
// 求交集:两个集合都有的元素
tags.intersection(popularTags);
// Set { 'javascript', 'webgpu' }
// 求并集:所有元素去重合并
tags.union(popularTags);
// Set { 'css', 'javascript', 'webgpu', 'typescript', 'ai' }
// 求差集:在 A 里但不在 B 里的元素
tags.difference(popularTags);
// Set { 'css', 'typescript' }
// 对称差集:在 A 或 B 里,但不同时在两者中
tags.symmetricDifference(popularTags);
// Set { 'css', 'typescript', 'ai' }
// 判断子集/超集
const small = new Set(['javascript', 'webgpu']);
small.isSubsetOf(tags); // true:small 的每个元素都在 tags 里
tags.isSupersetOf(small); // true:tags 包含 small 的所有元素
// 判断是否不相交
const other = new Set(['python', 'rust']);
tags.isDisjointFrom(other); // true:没有共同元素
方法接受任意可迭代对象,不只是 Set
参数不需要一定是 Set,任何有 size/has/keys 的对象都可以——Map 也可以直接传:
const rolePermissions = new Map([
['admin', ['read', 'write', 'delete']],
['editor', ['read', 'write']],
]);
const userPermissions = new Set(['read', 'write']);
// 直接用数组也可以
const required = ['read'];
userPermissions.isSupersetOf(required); // true
性能有讲究
intersection 内部迭代的是较小的那个集合,所以 smallSet.intersection(largeSet) 比反过来更快。求并集时,结果的顺序是”先 this 的所有元素,再 argument 里多出来的元素”。
SameValueZero 比较规则:NaN 等于 NaN,+0 等于 -0,对象按引用比较。
兼容性已经不需要等
这些方法在 2024 年 6 月进入 Baseline,现在覆盖情况是:Chrome/Edge 122+、Firefox 127+、Safari 17+、Node.js 22+。主流项目基本可以直接用,不用 polyfill。如果要兼容很老的浏览器,core-js 有完整实现。
下一步
去 MDN 看一下你项目中那几个手写的集合函数,现在有几个可以用原生 API 替换。一个简单判断:凡是写 for (const x of setA) if (setB.has(x)) 的逻辑,基本都可以换成 .intersection(setB) 一行搞定。
评论区
登录后可评论。