TC39JavaScript新語法Set集合操作

TC39 新語法深度解析(三):Set 新方法 — union、intersection、difference 終於原生支援

整理 TC39 Set Methods(Stage 4,已在所有主流瀏覽器支援)的七個新方法。用集合代數處理權限、標籤過濾、特徵標誌等常見場景。

· 3 分鐘閱讀

這是 TC39 新語法深度解析系列的第三篇。過去十年,我們處理 Set 之間的集合運算(合併、交叉、差集)需要繞路:用 spread + filter 或第三方工具函式。ES2024 後,七個新的 Set 方法讓這一切變成一行。


過去的做法:繞路的痛苦

const admins = new Set(['alice', 'bob', 'charlie']);
const moderators = new Set(['bob', 'charlie', 'david']);

// 過去合併兩個 Set(union)
const allModerators = new Set([...admins, ...moderators]);
// ['alice', 'bob', 'charlie', 'david']

// 過去交集(intersection)
const modsWhoAreAdmins = new Set(
  [...admins].filter(x => moderators.has(x))
);  // ['bob', 'charlie']

// 過去差集(admins 減去 moderators)
const adminsOnly = new Set(
  [...admins].filter(x => !moderators.has(x))
);  // ['alice']

這些 .filter() + .has() 的模式,到處重複——終於可以退休了。


七個新方法速覽

1. union(other):合併

const admins = new Set(['alice', 'bob', 'charlie']);
const moderators = new Set(['bob', 'charlie', 'david']);

const allStaff = admins.union(moderators);
console.log([...allStaff]);  // ['alice', 'bob', 'charlie', 'david']

2. intersection(other):交集

const modsWhoAreAdmins = moderators.intersection(admins);
console.log([...modsWhoAreAdmins]);  // ['bob', 'charlie']

3. difference(other):差集

const adminsOnly = admins.difference(moderators);
console.log([...adminsOnly]);  // ['alice']

4. symmetricDifference(other):對稱差集(只在其中一個集合的元素)

const exclusiveToOne = admins.symmetricDifference(moderators);
console.log([...exclusiveToOne]);  // ['alice', 'david']

唯讀查詢方法

// 5. isSubsetOf(other):是否為子集
const teamLeads = new Set(['alice']);
console.log(teamLeads.isSubsetOf(admins));  // true

// 6. isSupersetOf(other):是否為父集
console.log(admins.isSupersetOf(teamLeads));  // true

// 7. isDisjointFrom(other):是否無交集
const contractors = new Set(['eve', 'frank']);
console.log(contractors.isDisjointFrom(admins));  // true(無交集)

實際應用場景

權限系統

// 使用者的所有有效權限
function getEffectivePermissions(user) {
  const direct = new Set(user.directPermissions);
  const fromRoles = user.roles.flatMap(role => role.permissions);

  return fromRoles.reduce(
    (acc, perm) => acc.union(new Set([perm])),
    new Set(direct)
  );
}

// 檢查是否有某權限
const perms = getEffectivePermissions(currentUser);
console.log(perms.has('admin:delete'));  // true / false

// 檢查是否有一籃子權限(全都有)
const required = new Set(['posts:read', 'posts:write']);
console.log(required.isSubsetOf(perms));  // true(通過)

標籤過濾

// 文章的所有標籤
const article1Tags = new Set(['javascript', 'typescript', 'performance']);
const article2Tags = new Set(['typescript', 'react', 'css']);

// 共同標籤(推薦相關文章)
const relatedTags = article1Tags.intersection(article2Tags);
console.log([...relatedTags]);  // ['typescript']

// 排除已讀標籤後的推薦
const readTags = new Set(['javascript']);
const unreadRelatedTags = article2Tags.difference(readTags);
console.log([...unreadRelatedTags]);  // ['typescript', 'react', 'css']

特徵標誌(Feature Flags)

const enabledFlags = new Set(['new-checkout', 'dark-mode', 'beta-search']);
const userOverrides = new Set(['new-checkout:false', 'beta-search:false']);

function getEffectiveFlags(enabled, overrides) {
  const disabled = new Set(
    [...overrides]
      .filter(f => f.endsWith(':false'))
      .map(f => f.replace(':false', ''))
  );

  const enabledOnes = enabled.difference(disabled);
  return enabledOnes;
}

console.log([...getEffectiveFlags(enabledFlags, userOverrides)]);
// ['dark-mode'](被禁用的兩個已移除)

注意:isSubsetOf 等方法要求 Set 本身

// 這些唯讀方法是定義在 Set.prototype 上的
// 它們接受任何有 .size 和 .has() 的物件

const fakeSet = {
  size: 3,
  has: (x) => x === 'a'
};
// new Set(['a', 'b', 'c']).isSubsetOf(fakeSet) 
// 會呼叫 fakeSet.size 和 fakeSet.has()
// 這就是 Set Protocol——可以用於任何「類 Set」物件

瀏覽器支援

ES2024(即所有主流瀏覽器 2024 年版本)都已支援:

瀏覽器支援版本
Chrome122+(2024年3月)
Firefox127+(2024年7月)
Safari17.4+(2024年3月)
Node.js22.0+

可以直接在產品環境使用,無需 polyfill。


總結

Set 新方法是過去幾年 TC39 最受歡迎的提案之一——它解決的是我們每天都在面對的問題,只是以前沒有乾淨的原生解法。

// 從此告別這種:
[...set1].filter(x => set2.has(x))

// 直接用:
set1.intersection(set2)

這是 TC39 新語法深度解析系列的第三篇。下一篇:Promise.withResolvers — 更優雅地建立可控 Promise。


延伸閱讀


本文基於 TC39 Set Methods 提案(Stage 4,ES2024)整理。