ES2025 原生Set运算方法来了:用 union/intersection 一行搞定集合操作

以前想对两个 Set 做交集、并集、差集,你都得自己写循环或者转数组再 filter。虽然网上有各种一行代码的写法,但总归是“野路子”。这次 ES2025(实际上是 ES2024 年的提案,但今时今日已逐步落地)直接给 Set 加了一票原生方法,让我终于能甩掉工具函数了。

今天用实际场景好好聊聊 union()intersection()difference()symmetricDifference() 和几个判断方法。放心,每一个都有代码例子,直接用。

先看一眼新方法全家福

新的 Set 方法分为两类:返回新 Set 的,以及返回布尔值的。

  • union(other) —— 并集,返回包含两个集合所有元素的新 Set
  • intersection(other) —— 交集,返回同时存在于两个集合的元素
  • difference(other) —— 差集,返回存在于当前集合但不在 other 中的元素
  • symmetricDifference(other) —— 对称差集,返回只存在于其中一个集合的元素(相当于全集减去交集)
  • isSubsetOf(other) —— 是否是 another 的子集
  • isSupersetOf(other) —— 是否包含 another 的全部元素
  • isDisjointFrom(other) —— 是否和 another 没有交集

还有一个额外的方法 toSorted() ?那是数组的,不是本文重点。咱们就盘 Set 的这些。

快速上手:一个权限系统的例子

假设你有一个用户权限列表,用一个 Set 存储。现在有个操作需要某些权限,你却要判断用户是否有交集?过去可能写一堆,现在直接一行。

看下面这段代码,不用任何 polyfill,只是把每个方法串起来。

// 用户当前拥有的权限
const userPerms = new Set(['read', 'write', 'share', 'export']);
// 执行某个高危操作需要的权限
const requiredPerms = new Set(['read', 'delete', 'admin']);

// 1. 检查用户是否有任一高危权限(交集非空)
const canDoHighRisk = !userPerms.intersection(requiredPerms).size;
console.log(canDoHighRisk); // false,因为 read 是交集,所以结果为 false

// 2. 检查用户是否满足所有权限(是 requiredPerms 的超集)
const isAdmin = userPerms.isSupersetOf(requiredPerms);
console.log(isAdmin); // false

// 3. 用户还没有的权限(差集)
const missingPerms = requiredPerms.difference(userPerms);
console.log([...missingPerms]); // ["delete", "admin"]

// 4. 把两个权限集合合并成一个更大的集合(并集)
const allPerms = userPerms.union(requiredPerms);
console.log([...allPerms]); // ["read", "write", "share", "export", "delete", "admin"]

// 5. 对称差集:看看双方有哪些不同的权限
const diffPerms = userPerms.symmetricDifference(requiredPerms);
console.log([...diffPerms]); // ["write", "share", "export", "delete", "admin"]
    

直接用浏览器最新的 Chrome 或 Node 22+ 能跑。如果你想在旧环境用,建议装一个 core-js 的对应 polyfill,但这里咱们只聊原生。

实战:筛选出没有重叠的用户标签

你有一个用户 ID 列表,另一个是“已经下发优惠券”的用户 ID 列表。你想知道哪些用户从未收到过券,也想知道哪些用户既领了券又可能参加活动。这类逻辑过去要写一堆循环,现在就是 Set 的一顿饭。

// 注册用户 ID
const registered = new Set(['001', '002', '003', '004']);
// 已发过优惠券的用户 ID
const couponed = new Set(['003', '004', '005', '006']);

// 从未收到过券的用户(注册用户 - 已发券用户)
const noCouponUsers = registered.difference(couponed);
console.log('从未收券:', [...noCouponUsers]); // ["001", "002"]

// 既注册又领了券的用户(交集)
const both = registered.intersection(couponed);
console.log('重叠人群:', [...both]); // ["003", "004"]

// 两个群体中所有不重复的人(并集)
const allUsers = registered.union(couponed);
console.log('全部人数:', [...allUsers]); // ["001", "002", "003", "004", "005", "006"]

// 只注册未领券 + 只领券未注册(对称差)
const onlyOneSide = registered.symmetricDifference(couponed);
console.log('只占一头:', [...onlyOneSide]); // ["001", "002", "005", "006"]
    

一个很大的改进点:这些方法返回的是 新 Set,不会修改原来的 Set。也就是说你不用担心误改数据。

几个容易忽略的细节

这些新方法接收的参数可以是 Set,也可以是任何“类 Set 对象”(比如数组?不,文档标准是要求一个具有 sizekeys() 或者迭代器的对象)。但我测试中传数组会报错,所以还是老老实实传 Set。不过某些引擎里提供了更宽松的行为,最好传 Set。

值比较遵循 SameValueZero 规则,也就是说 NaNNaN 是相等的,0-0 也是相等的。这对 Set 本来就默认,所以很自然。

还有一个特点:这些方法都讲究“主次顺序”。比如 union() 返回的 Set 会先包含调用者里的元素,再包含传参里的元素;difference() 自然是以调用者为主。如果你关心顺序,可以把主体放在左边。

性能?别担心了

原生方法大概率比你手写的 for 循环要快,因为引擎可以直接用内部数据结构操作,避免迭代器开销。而且它们会提前计算大小并且自动跳过重复添加。比如 union 本质上就是把两个哈希表合并,引擎如果检测到两个 Set 的底层哈希表同构,有机会直接做批量操作。这些性能收益只有在超大集合上才能感觉出来,但至少不会成为短板。

一个真实的业务场景:过滤周末和休息日

假设你有一个“可预约日期”的 Set,还有一个“节假日”的 Set,需要算出真正可预约的工作日。这样写是不是很漂亮?

const availableDates = new Set([
    '2025-06-02',
    '2025-06-03',
    '2025-06-04',
    '2025-06-05',
    '2025-06-06',
    '2025-06-07',
    '2025-06-08'
]);

const holidays = new Set([
    '2025-06-04',
    '2025-06-05'
]);

const workDays = availableDates.difference(holidays);
console.log('可预约工作日:', [...workDays]);
// ["2025-06-02", "2025-06-03", "2025-06-06", "2025-06-07", "2025-06-08"]
    

注意这里周六日也算可预约,因为咱们没排除周末,反正就是说这方法很清晰。

isSubsetOf / isSupersetOf 的判断妙用

在做权限校验时,经常需要判断“请求要求的权限是否属于用户权限集合的子集”,直接用 required.isSubsetOf(userPerms) 就完美了。

function canAccess(userPerms, requiredPerms) {
    return requiredPerms.isSubsetOf(userPerms);
}
const role1 = new Set(['read', 'write']);
const role2 = new Set(['read', 'write', 'execute']);
console.log(canAccess(role1, role2)); // false,因为 role2 不是 role1 的子集
console.log(canAccess(role2, role1)); // true
    

什么时候别用这些方法?

如果你是处理基本类型(字符串、数字),那没问题。但如果你的 Set 里存的是对象,而且你希望按照引用比较,那也 OK。但是如果你需要深比较,那还得自己用循环。另外在旧版本浏览器中,你可能需要这种 fallback 写法,但这不是今天主题。

有人会问,这些方法能用在 Node 的 Set 上吗?可以,只要 Node 22.3.0 以上(大概),Node 18 和 20 可能没得,建议直接用最新版。

一行代码兼容旧环境?

因为新方法还没有完全覆盖所有运行时,实际开发中你又想用,可以手动扩展 Set 原型,但不推荐。我更推荐你使用 core-js/actual/set/union 等按需引入 polyfill,或者干脆用 Babel 编译。但是自己写个简易的也不难,比如:

if (!Set.prototype.union) {
    Set.prototype.union = function(other) {
        return new Set([...this, ...other]);
    };
}
    

但这种简易 polyfill 性能略慢,而且很多方法容易写错,比如 difference 要避免重复。所以还是等标准普及吧。

最终总结

ES2025 给 Set 添加的这些方法,不是花架子,是从 Python、Ruby 这些语言里吸取了经验,真正补充了 JavaScript 集合操作的短板。以后遇到“求交集”“校验包含”这种需求,咱们就不用再满脑子转 filterincludes 了。你看,我写权限的时候,思维都跟着简洁了很多。

下次见到你的小伙伴还在手写集合运算,直接把这篇丢给他。

完。

ES2025 原生Set运算方法来了:用 union/intersection 一行搞定集合操作
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请发送邮件1506151422@qq.com联系,将会及时下架删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 javascript ES2025 原生Set运算方法来了:用 union/intersection 一行搞定集合操作 https://www.taomawang.com/web/javascript/2663.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务