12k
All articles

Ein praktischer Leitfaden zu den neuen Set-Methoden in JavaScript

JavaScripts Set-Methoden erklärt: union, intersection, difference, symmetricDifference und Teilmengenprüfungen, plus Map-Unterstützung und Browser-Support.

OpenReplay Team
OpenReplay Team
Ein praktischer Leitfaden zu den neuen Set-Methoden in JavaScript

Das Set-Objekt in JavaScript hat sieben neue Instanzmethoden erhalten — union(), intersection(), difference(), symmetricDifference(), isSubsetOf(), isSupersetOf() und isDisjointFrom() — die seit dem 11. Juni 2024 als Baseline Newly Available gelten. Diese Methoden ersetzen die Array.filter() + Array.includes()-Muster, die Frontend-Entwickler jahrelang manuell implementiert haben, um Collections zu vergleichen — und sie tun dies mit mengenbasierten Lookups anstelle von verschachtelten Array-Durchläufen. Wenn Sie die ES2015-Set-API bereits kennen — add, has, delete, Iteration — liefert Ihnen dieser Leitfaden genau das, was die Ankündigungsbeiträge ausgelassen haben: das Set-ähnliche Argumentprotokoll, das es ermöglicht, eine Map zu übergeben, die Referenzgleichheits-Falle, die Objektvergleiche lautlos zum Scheitern bringt, direkt einsetzbare Frontend-Muster und eine ehrliche Browser-Kompatibilitätstabelle.

Wichtigste Erkenntnisse

  • Alle sieben neuen Set-Methoden sind seit dem 11. Juni 2024 als Baseline Newly Available verfügbar und wurden in Chrome 122, Edge 122, Firefox 127 und Safari 17 sowie in Node.js 22.0.0 ausgeliefert — für aktuelle Zielumgebungen ist kein Polyfill erforderlich.
  • Das Argument dieser Methoden muss keine Set-Instanz sein; es muss eine numerische size-Eigenschaft, eine aufrufbare has-Methode und eine aufrufbare keys-Methode bereitstellen — das bedeutet, dass eine Map direkt verwendet werden kann.
  • Set vergleicht Werte per Referenz, daher gibt new Set([{id:1}]).intersection(new Set([{id:1}])) ein leeres Set zurück — verwenden Sie stattdessen Sets aus stabilen primitiven IDs.
  • difference() ist nicht kommutativ, und symmetricDifference() gibt zuerst die nur im Receiver vorhandenen Elemente in der Reihenfolge des Receivers zurück.
  • Das Ersetzen von [...a].filter(x => b.includes(x)) durch a.intersection(b) tauscht ein O(n²)-Array-Muster gegen eines aus, das gemäß der Spezifikation mit dem kleineren Set und durchschnittlich sublinearem Set-Zugriff skaliert.

Die sieben Methoden im Überblick

Die neuen Methoden lassen sich in zwei Gruppen unterteilen: vier, die ein neues Set zurückgeben, und drei, die einen booleschen Wert zurückgeben. Diese Unterscheidung ist das nützlichste mentale Modell — sie zeigt sofort, ob Sie eine Collection aufbauen oder eine Ja/Nein-Frage stellen.

Operationen, die ein neues Set zurückgeben:

Prädikate, die einen booleschen Wert zurückgeben:

Der Proposal, der diese Methoden eingeführt hat, ist der TC39 Set methods proposal, der Stage 4 erreicht hat, inzwischen inaktiv ist, in die ECMAScript-Spezifikation aufgenommen wurde und Teil von ECMAScript 2025 ist.

Die Set-zurückgebenden Methoden in echtem Frontend-Code

Die vier Set-zurückgebenden Methoden — union(), intersection(), difference() und symmetricDifference() — geben jeweils ein neues Set zurück, ohne einen der Eingabewerte zu verändern, und jede lässt sich direkt auf eine Aufgabe abbilden, die Frontend-Entwickler bereits manuell erledigen.

union(): Feature-Flag-Overrides mit Standardwerten zusammenführen

union() gibt ein neues Set zurück, das alle Elemente aus beiden Sets enthält, wobei Duplikate entfernt werden.

const defaults = new Set(["new-dashboard", "dark-mode"]);
const overrides = new Set(["dark-mode", "beta-search"]);

const enabled = defaults.union(overrides);
// Set(3) { "new-dashboard", "dark-mode", "beta-search" }

Die klassische Variante war new Set([...defaults, ...overrides]). union() drückt die Absicht direkt aus. Wenn Sie einen Basissatz aktivierter Feature-Flags mit benutzer- oder umgebungsspezifischen Overrides zusammenführen, liefert union() den effektiven Flag-Satz in einem einzigen Aufruf.

intersection(): Ermitteln, auf welche Routen ein Benutzer tatsächlich zugreifen kann

intersection() gibt ein neues Set zurück, das nur die Elemente enthält, die in beiden Sets vorhanden sind.

const requiredForRoute = new Set<string>(["billing:read", "billing:write"]);
const userPermissions = new Set<string>(["billing:read", "users:read"]);

const satisfied = requiredForRoute.intersection(userPermissions);
// Set(1) { "billing:read" }

Für die Routenzugriffskontrolle schneiden Sie den Berechtigungssatz, den eine Route erfordert, mit dem Berechtigungssatz des Benutzers. Die Größe des Ergebnisses im Verhältnis zum erforderlichen Set zeigt Ihnen, ob der Zugriff teilweise oder vollständig gewährt wird.

difference(): Änderungen in einer Mehrfachauswahl verfolgen

difference() gibt ein neues Set zurück, das Elemente enthält, die in diesem Set, aber nicht im anderen vorhanden sind — und es ist nicht kommutativ.

const prevSelected = new Set<string>(["a", "b", "c"]);
const nextSelected = new Set<string>(["b", "c", "d"]);

const added = nextSelected.difference(prevSelected);   // Set(1) { "d" }
const removed = prevSelected.difference(nextSelected); // Set(1) { "a" }

Bei Mehrfachauswahl-Komponenten liefert nextSelected.difference(prevSelected) die gerade hinzugefügten Elemente und prevSelected.difference(nextSelected) die gerade entfernten — zwei Set-Operationen ersetzen ein Muster, das Sortierung oder verschachtelte Schleifen erforderte. a.difference(b) gibt Elemente zurück, die in a, aber nicht in b vorhanden sind, während b.difference(a) das Inverse zurückgibt; die Reihenfolge der Argumente bestimmt die Operation.

symmetricDifference(): Geänderte Schlüssel zwischen zwei Snapshots hervorheben

symmetricDifference() gibt ein neues Set zurück, das Elemente enthält, die in einem der Sets, aber nicht in beiden vorhanden sind.

const before = new Set<string>(["name", "email", "phone"]);
const after = new Set<string>(["name", "email", "address"]);

const changedKeys = before.symmetricDifference(after);
// Set(2) { "phone", "address" }

Um hervorzuheben, welche Schlüssel zwischen zwei Snapshots eines State-Objekts hinzugekommen oder weggefallen sind, nehmen Sie die symmetrische Differenz ihrer Schlüssel-Sets. Ein Detail, das die Ankündigungsbeiträge übergehen: Die Iterationsreihenfolge hängt vom Receiver ab. Gemäß ECMA-262 2025 gibt symmetricDifference() zuerst die nur im Receiver vorhandenen Elemente in der Reihenfolge des Receivers zurück, gefolgt von den nur im anderen Set vorhandenen Elementen in der Reihenfolge von other.keys(). Die Menge der Elemente ist unabhängig davon, von welcher Seite aus die Methode aufgerufen wird, identisch; die Reihenfolge hingegen nicht.

Die booleschen Prädikate für Autorisierungsprüfungen

Die drei Prädikatmethoden — isSubsetOf(), isSupersetOf() und isDisjointFrom() — geben jeweils einen booleschen Wert zurück und lassen sich jeweils auf eine gängige Autorisierungs- oder Eingabevalidierungsprüfung abbilden.

isSupersetOf(): Alle erforderlichen Scopes prüfen

isSupersetOf() gibt true zurück, wenn dieses Set jedes Element des angegebenen Sets enthält.

const grantedScopes = new Set<string>(["read", "write", "delete"]);
const requiredScopes = new Set<string>(["read", "write"]);

const hasAllRequiredScopes = grantedScopes.isSupersetOf(requiredScopes);
// true

Um zu prüfen, ob die einem Benutzer gewährten OAuth-Scopes alle für eine Operation erforderlichen Scopes abdecken, gibt grantedScopes.isSupersetOf(requiredScopes) in einem einzigen Aufruf true zurück — äquivalent zu [...requiredScopes].every(s => grantedScopes.has(s)), aber als Mengenbeziehung ausgedrückt.

isSubsetOf(): Prüfen, ob eine Tag-Liste vollständig unterstützt wird

isSubsetOf() gibt true zurück, wenn jedes Element dieses Sets im angegebenen Set enthalten ist.

const supportedTags = new Set<string>(["sale", "new", "featured", "clearance"]);
const requestedTags = new Set<string>(["sale", "new"]);

const allSupported = requestedTags.isSubsetOf(supportedTags);
// true

Wenn ein Aufrufer eine Liste von Tags oder Filtern übergibt, bestätigt requestedTags.isSubsetOf(supportedTags), dass jedes davon erkannt wird, bevor die Abfrage ausgeführt wird.

isDisjointFrom(): Konflikte zwischen Modifikatortasten erkennen

isDisjointFrom() gibt true zurück, wenn die beiden Sets keine gemeinsamen Elemente haben.

const pressedKeys = new Set<string>(["Meta", "Shift"]);
const conflictingModifiers = new Set<string>(["Control", "Alt"]);

const noConflict = pressedKeys.isDisjointFrom(conflictingModifiers);
// true

Bei der Verarbeitung von Tastaturkürzeln ermöglicht isDisjointFrom() zu prüfen, ob keine konfliktierenden Modifikatortasten gedrückt sind, bevor eine Aktion ausgelöst wird.

Das Set-ähnliche Protokoll: Das Argument muss kein Set sein

Das Argument für jede dieser Methoden muss keine Set-Instanz sein — es muss ein Objekt mit einer numerischen size-Eigenschaft, einer aufrufbaren has-Methode und einer aufrufbaren keys-Methode sein. Eine Map erfüllt diese Anforderung nativ, was bedeutet, dass mySet.intersection(myMap) gültig ist und gegen die Schlüssel der Map prüft. Jeder Ankündigungsbeitrag beschreibt das Argument als „ein anderes Set”, was technisch gesehen unvollständig ist.

Dieses Protokoll ist in der ECMA-262-Spezifikation unter GetSetRecord definiert, und MDN dokumentiert die Anforderungen an Set-ähnliche Objekte direkt.

const map = new Map([
  ["a", 1],
  ["b", 2],
]);
const set = new Set(["a", "c"]);

set.intersection(map);
// Set(1) { "a" }  — prüft gegen die Schlüssel der Map

Bestätigt in Node v22.16.0: set.intersection(map) gibt Set { 'a' } zurück, weil "a" das einzige Element des Sets ist, das auch unter den Schlüsseln der Map vorkommt. Dies ist für die Interoperabilität relevant: Sie können ein Set gegen die Schlüssel einer Map schneiden, ohne ein zwischengeschaltetes new Set(map.keys()) zu erstellen, und jede Bibliothek für unveränderliche Collections oder jedes benutzerdefinierte Index-Objekt, das size, has und keys bereitstellt, lässt sich ohne Konvertierung in diese Methoden einbinden.

Die Objektidentitäts-Falle

Set vergleicht Werte per Referenz, nicht per Struktur. MDNs Dokumentation zur Wertgleichheit bestätigt dies. Die praktische Konsequenz ist eine Falle, wenn Ihr Set Objekte enthält:

new Set([{ id: 1 }]).intersection(new Set([{ id: 1 }]));
// Set(0) {}

Die Referenzgleichheit ist die Falle: Dies gibt ein leeres Set zurück, weil die beiden {id: 1}-Objekte unterschiedliche Referenzen sind, auch wenn sie identisch aussehen. Die Lösung besteht darin, Sets aus stabilen primitiven IDs zu schneiden und die Objekte anschließend aus einer Lookup-Map wiederherzustellen:

type User = { id: number; name: string };

const prev: User[] = [{ id: 1, name: "Ada" }, { id: 2, name: "Lin" }];
const next: User[] = [{ id: 2, name: "Lin" }, { id: 3, name: "Mo" }];

const byId = new Map(next.map((u) => [u.id, u]));

const prevIds = new Set(prev.map((u) => u.id));
const nextIds = new Set(next.map((u) => u.id));

const addedIds = nextIds.difference(prevIds); // Set(1) { 3 }
const added = [...addedIds].map((id) => byId.get(id)!);
// [{ id: 3, name: "Mo" }]

Referenzgleichheitsfehler dieser Art hinterlassen keinen Stack-Trace. Die Set-Operation wird fehlerfrei abgeschlossen, gibt ein leeres Set zurück, und die Benutzeroberfläche aktualisiert sich schlicht nicht — eine Mehrfachauswahl, die sich nicht ändert, ein Berechtigungs-Badge, das sich nicht zurücksetzt, eine Diff-Ansicht, die keine Änderungen anzeigt. Da keine Exception ausgelöst wird, taucht der Fehler auch nicht im Error-Monitoring auf. Session Replay ist eine Technik, die diese Klasse von stillen Fehlern sichtbar macht: Sie beobachten, wie der Benutzer interagiert, die Operation abgeschlossen wird und die Oberfläche auf nichts reagiert.

Performance: Warum dies filter + includes übertrifft

Das Ersetzen von [...a].filter(x => b.includes(x)) durch a.intersection(b) ist nicht nur ein Gewinn für die Lesbarkeit. Array.includes() ist O(n), was das Filter-Muster über zwei n-elementige Collections zu O(n²) macht, während intersection() mit dem kleineren der beiden Sets skaliert, vorausgesetzt, der durchschnittlich sublineare Set-Zugriff, den die Spezifikation vorschreibt, ist gegeben. Der ECMA-262-Abschnitt zu Set-Objekten schreibt vor, dass Set-Zugriffe im Durchschnitt sublinear ausgeführt werden müssen — garantiert wird O(1) nicht — und die MDN-Referenz zu intersection() beschreibt das Skalierungsverhalten in Abhängigkeit vom kleineren Set.

OperationArray-MusterKomplexitätNative Set-MethodeKomplexität
Intersection[...a].filter(x => b.includes(x))O(n²)a.intersection(b)skaliert mit kleinerem Set, durchschn. sublinearer Zugriff
Unionnew Set([...a, ...b])O(n + m)a.union(b)O(n + m)
Difference[...a].filter(x => !b.includes(x))O(n²)a.difference(b)skaliert mit a, durchschn. sublinearer Zugriff

Der Unterschied wächst mit der Collection-Größe: Bei wenigen Elementen ist er vernachlässigbar, aber die Array-Muster degradieren quadratisch, während die nativen Methoden dies nicht tun.

Migrationsrezepte

Ordnen Sie Ihren bestehenden Array-Vergleichscode direkt den neuen Methoden zu. Diese drei Ersetzungen decken den Großteil der realen Anwendungsfälle ab.

Altes MusterNeue Methode
[...a].filter(x => b.includes(x))a.intersection(b)
[...new Set([...a, ...b])]a.union(b) (gibt ein Set zurück)
[...a].filter(x => !b.includes(x))a.difference(b)

Wenn Ihre Daten in Arrays vorliegen, wrappen Sie jede Seite einmalig in new Set(...), führen Sie die Operation aus und spreaden Sie das Ergebnis zurück in ein Array, wenn eine nachgelagerte API dies erfordert:

const a = ["x", "y", "z"];
const b = ["y", "z", "w"];

const common = [...new Set(a).intersection(new Set(b))];
// ["y", "z"]

Die Konvertierung in ein Set ist selbst O(n), aber der Umweg vermeidet den verschachtelten Scan des O(n²)-filter + includes-Musters und skaliert besser, wenn die Collections wachsen.

Browser-Unterstützung und Polyfills

Alle sieben Set-Methoden sind seit dem 11. Juni 2024 als Baseline Newly Available verfügbar und wurden in Chrome 122 (20. Feb. 2024), Edge 122 (23. Feb. 2024), Firefox 127 (11. Jun. 2024) und Safari 17 (18. Sep. 2023) ausgeliefert — für aktuelle Browser-Zielumgebungen ist kein Polyfill erforderlich. Sie sind außerdem in Node.js 22.0.0 (24. Apr. 2024) über V8 12.4 verfügbar.

UmgebungVersionVeröffentlichungsdatum
Chrome12220. Feb. 2024
Edge12223. Feb. 2024
Firefox12711. Jun. 2024
Safari1718. Sep. 2023
Node.js22.0.0 (V8 12.4)24. Apr. 2024

Für ältere Zielumgebungen bieten die Bibliothek core-js und das Projekt es-shims spezifikationskonforme Polyfills. Wenn Sie ausschließlich aktuelle Evergreen-Browser und Node.js 22+ unterstützen, können Sie auf den Polyfill vollständig verzichten.

Wie es weitergeht

Durchsuchen Sie Ihre Codebasis nach filter + includes- und filter + !includes-Mustern über deduplizierten Daten — das sind die direkten Kandidaten für intersection() und difference(). Bevor Sie Collections mit Objekten migrieren, wechseln Sie zu Sets aus stabilen primitiven IDs, um die Referenzgleichheits-Falle zu vermeiden, und denken Sie daran, dass jede Map, unveränderliche Collection oder jedes benutzerdefinierte Index-Objekt, das size, has und keys bereitstellt, direkt als Argument übergeben werden kann. Die Methoden sind Baseline, die Semantik ist spezifikationsstabil, und die Array-Muster, die sie ersetzen, waren nie so kostengünstig, wie sie aussahen.

FAQs

Kann ich eine Map direkt an Set.intersection() übergeben oder muss ich sie zuerst konvertieren?

Sie können eine Map direkt übergeben. Die neuen Set-Methoden akzeptieren jedes Set-ähnliche Objekt, das in der ECMA-262-Spezifikation als Objekt mit einer numerischen size-Eigenschaft, einer aufrufbaren has-Methode und einer aufrufbaren keys-Methode definiert ist. Eine Map erfüllt diese Anforderungen nativ, sodass mySet.intersection(myMap) gegen die Schlüssel der Map prüft, ohne ein zwischengeschaltetes new Set(map.keys()) zu erstellen. Sets aus Bibliotheken für unveränderliche Collections und benutzerdefinierte Index-Objekte, die dieselben drei Member bereitstellen, funktionieren ebenfalls ohne Konvertierung.

Warum gibt Set intersection ein leeres Set zurück, wenn beide Sets identische Objekte enthalten?

Weil Set Werte per Referenz vergleicht, nicht per Struktur. Der Ausdruck new Set([{id:1}]).intersection(new Set([{id:1}])) gibt ein leeres Set zurück, da die beiden {id:1}-Objekte unterschiedliche Referenzen sind, auch wenn sie identisch aussehen — bestätigt in Node v22.16.0. Die Lösung besteht darin, Sets aus stabilen primitiven IDs zu erstellen, die Operation darauf auszuführen und die übereinstimmenden Objekte anschließend aus einer nach ID indizierten Lookup-Map wiederherzustellen.

Was ist der Unterschied zwischen difference() und symmetricDifference()?

difference() ist gerichtet und nicht kommutativ: a.difference(b) gibt Elemente zurück, die in a, aber nicht in b vorhanden sind, während b.difference(a) das Inverse zurückgibt. symmetricDifference() gibt Elemente zurück, die in einem der Sets, aber nicht in beiden vorhanden sind, und ist inhaltlich reihenfolgeunabhängig. Auch ihre Iterationsreihenfolgen unterscheiden sich: symmetricDifference() gibt zuerst die nur im Receiver vorhandenen Elemente in der Reihenfolge des Receivers zurück, gefolgt von den nur im anderen Set vorhandenen Elementen in der Reihenfolge von other.keys(). Die Menge der Ergebnisse ist unabhängig davon, von welcher Seite aus die Methode aufgerufen wird, identisch; die Reihenfolge hingegen nicht.

Benötige ich für die neuen Set-Methoden in der Produktion noch einen Polyfill?

Nicht für aktuelle Browser-Zielumgebungen. Alle sieben Methoden sind seit dem 11. Juni 2024 als Baseline Newly Available verfügbar und wurden in Chrome 122, Edge 122, Firefox 127, Safari 17 und Node.js 22.0.0 über V8 12.4 ausgeliefert. Wenn Sie ausschließlich aktuelle Evergreen-Browser und Node.js 22 oder höher unterstützen, können Sie auf den Polyfill vollständig verzichten. Für ältere Zielumgebungen bieten die Bibliothek core-js und das Projekt es-shims spezifikationskonforme Polyfills, die Sie selektiv einbinden können.

Open-source session replay

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.