你以为求交集只能手写循环,今天 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) 一行搞定。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 16 阅读