Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten.Erfahre mehr über dieses Experiment.
WeakMap
Baseline Widely available *
This feature is well established and works across many devices and browser versions. It’s been available across browsers since Juli 2015.
* Some parts of this feature may have varying levels of support.
EinWeakMap ist eine Sammlung von Schlüssel/Wert-Paaren, bei der die Schlüssel Objekte odernicht-registrierte Symbole sein müssen, mit Werten beliebigenJavaScript-Typs. Es werden keine starken Referenzen auf die Schlüssel erstellt. Das bedeutet, dass die Anwesenheit eines Objekts als Schlüssel in einerWeakMap nicht verhindert, dass das Objekt vom Garbage Collector gesammelt wird. Sobald ein Objekt, das als Schlüssel verwendet wird, gesammelt wurde, werden seine entsprechenden Werte in jederWeakMap ebenfalls Kandidaten für die Garbage Collection — solange sie nicht anderswo stark referenziert sind. Der einzige primitive Typ, der als Schlüssel einerWeakMap verwendet werden kann, ist das Symbol — genauer gesagt,nicht-registrierte Symbole —, da nicht-registrierte Symbole garantiert einzigartig sind und nicht erneut erstellt werden können.
WeakMap ermöglicht das Verknüpfen von Daten mit Objekten auf eine Weise, die nicht verhindert, dass die Schlüsselobjekte gesammelt werden, selbst wenn die Werte die Schlüssel referenzieren. EineWeakMap erlaubt jedoch nicht die Beobachtung der Lebendigkeit ihrer Schlüssel, weshalb sie keine Enumeration zulässt; wenn eineWeakMap irgendeine Methode bereitstellen würde, um eine Liste ihrer Schlüssel zu erhalten, würde die Liste vom Zustand der Garbage Collection abhängen und Nicht-Determinismus einführen. Wenn Sie eine Liste von Schlüsseln haben möchten, sollten Sie eherMap als eineWeakMap verwenden.
Sie können mehr über dieWeakMap im AbschnittWeakMap-Objekt desLeitfadens zu Schlüssel-Sammlungen erfahren.
In diesem Artikel
Beschreibung
Die Schlüssel von WeakMaps müssen Garbage-Collectable sein. Die meistenprimitiven Datentypen können willkürlich erstellt werden und haben keine Lebensdauer, sodass sie nicht als Schlüssel verwendet werden können. Objekte undnicht-registrierte Symbole können als Schlüssel verwendet werden, da sie Garbage-Collectable sind.
Schlüsselgleichheit
Wie bei regulärenMap, basiert die Wertgleichheit auf demSameValueZero-Algorithmus, der dem===-Operator entspricht, daWeakMap nur Objekt- und Symbolschlüssel halten kann. Das bedeutet, dass die Gleichheit für Objektschlüssel auf der Objektidentität basiert. Sie werden durchReferenz und nicht durch Wert verglichen.
Warum WeakMap?
Eine Karten-APIkönnte in JavaScript mit zwei Arrays (einem für Schlüssel, einem für Werte) implementiert werden, die von den vier API-Methoden gemeinsam genutzt werden. Das Hinzufügen von Elementen auf dieser Karte würde gleichzeitig das Anhängen eines Schlüssels und eines Wertes an das Ende jedes dieser Arrays beinhalten. Dadurch würden die Indizes des Schlüssels und des Wertes mit beiden Arrays korrespondieren. Das Abrufen von Werten von der Karte würde das Durchlaufen aller Schlüssel zum Finden eines Übereinstimmung erfordern und dann den Index dieser Übereinstimmung verwenden, um den entsprechenden Wert aus dem Array der Werte abzurufen.
Eine solche Implementierung hätte zwei wesentliche Nachteile:
- Der erste ist ein
O(n)für das Setzen und Suchen (n ist die Anzahl der Schlüssel in der Karte), da beide Operationen die Liste der Schlüssel durchlaufen müssen, um einen passenden Wert zu finden. - Der zweite Nachteil ist ein Speicherleck, weil die Arrays sicherstellen, dass Referenzen auf jeden Schlüssel und jeden Wert auf unbestimmte Zeit aufrechterhalten werden. Diese Referenzen verhindern, dass die Schlüssel vom Garbage Collector gesammelt werden, selbst wenn es keine anderen Referenzen auf das Objekt gibt. Dies würde auch verhindern, dass die entsprechenden Werte gesammelt werden.
Im Gegensatz dazu referenziert ein Schlüsselobjekt in einerWeakMap seine Inhalte stark, solange der Schlüssel nicht gesammelt wird, danach jedoch nur noch schwach. EineWeakMap:
- verhindert nicht die Garbage Collection, die letztendlich Referenzen auf das Schlüsselobjekt entfernt
- erlaubt die Garbage Collection jeglicher Werte, wenn ihre Schlüsselobjekte nicht von einer Stelle außerhalb der
WeakMapreferenziert werden
EineWeakMap kann eine besonders nützliche Konstruktion sein, wenn Schlüssel Informationen zugeordnet werden, dienur dann wertvoll sind, wenn der Schlüssel nicht vom Garbage Collector gesammelt wurde.
Da eineWeakMap jedoch nicht die Lebendigkeit ihrer Schlüssel beobachten lässt, sind ihre Schlüssel nicht aufzählbar. Es gibt keine Methode, eine Liste der Schlüssel zu erhalten. Wenn es eine gäbe, würde die Liste vom Zustand der Garbage Collection abhängen und Nicht-Determinismus einführen. Wenn Sie eine Liste von Schlüsseln haben möchten, sollten Sie eineMap verwenden.
Konstruktor
WeakMap()Erstellt ein neues
WeakMap-Objekt.
Instanzeigenschaften
Diese Eigenschaften sind aufWeakMap.prototype definiert und werden von allenWeakMap-Instanzen geteilt.
WeakMap.prototype.constructorDie Konstruktorfunktion, die das Instanzobjekt erstellt hat. Für
WeakMap-Instanzen ist der Anfangswert derWeakMap-Konstruktor.WeakMap.prototype[Symbol.toStringTag]Der anfängliche Wert der
[Symbol.toStringTag]-Eigenschaft ist der String"WeakMap". Diese Eigenschaft wird inObject.prototype.toString()verwendet.
Instanzmethoden
WeakMap.prototype.delete()Entfernt den Eintrag, der durch den Schlüssel in dieser
WeakMapangegeben wird.WeakMap.prototype.get()Gibt den Wert zurück, der dem Schlüssel in dieser
WeakMapentspricht, oderundefined, wenn keiner vorhanden ist.WeakMap.prototype.getOrInsert()Gibt den Wert zurück, der dem angegebenen Schlüssel in dieser
WeakMapentspricht. Ist der Schlüssel nicht vorhanden, fügt es einen neuen Eintrag mit dem Schlüssel und einem gegebenen Standardwert ein und gibt den eingefügten Wert zurück.WeakMap.prototype.getOrInsertComputed()Gibt den Wert zurück, der dem angegebenen Schlüssel in dieser
WeakMapentspricht. Ist der Schlüssel nicht vorhanden, fügt es einen neuen Eintrag mit dem Schlüssel und einem Standardwert, der aus einem gegebenen Rückruf berechnet wird, ein und gibt den eingefügten Wert zurück.WeakMap.prototype.has()Gibt einen booleschen Wert zurück, der angibt, ob ein Eintrag mit dem angegebenen Schlüssel in dieser
WeakMapexistiert oder nicht.WeakMap.prototype.set()Fügt einen neuen Eintrag mit einem angegebenen Schlüssel und Wert zu dieser
WeakMaphinzu oder aktualisiert einen vorhandenen Eintrag, wenn der Schlüssel bereits existiert.
Beispiele
>Verwendung von WeakMap
const wm1 = new WeakMap();const wm2 = new WeakMap();const wm3 = new WeakMap();const o1 = {};const o2 = () => {};const o3 = window;wm1.set(o1, 37);wm1.set(o2, "azerty");wm2.set(o1, o2); // a value can be anything, including an object or a functionwm2.set(o2, undefined);wm2.set(wm1, wm2); // keys and values can be any objects. Even WeakMaps!wm1.get(o2); // "azerty"wm2.get(o2); // undefined, because that is the set valuewm2.get(o3); // undefined, because there is no key for o3 on wm2wm1.has(o2); // truewm2.has(o2); // true (even if the value itself is 'undefined')wm2.has(o3); // falsewm3.set(o1, 37);wm3.get(o1); // 37wm1.has(o1); // truewm1.delete(o1);wm1.has(o1); // falseImplementierung einer WeakMap-ähnlichen Klasse mit einer .clear()-Methode
class ClearableWeakMap { #wm; constructor(init) { this.#wm = new WeakMap(init); } clear() { this.#wm = new WeakMap(); } delete(k) { return this.#wm.delete(k); } get(k) { return this.#wm.get(k); } has(k) { return this.#wm.has(k); } set(k, v) { this.#wm.set(k, v); return this; }}Emulieren privater Mitglieder
Entwickler können eineWeakMap verwenden, um private Daten einem Objekt zuzuordnen, mit folgenden Vorteilen:
- Im Vergleich zu einer
Maphält eine WeakMap keine starken Referenzen zu dem Objekt, das als Schlüssel verwendet wird, sodass die Metadaten dieselbe Lebensdauer wie das Objekt selbst haben und Speicherlecks vermieden werden. - Im Vergleich zur Verwendung von nicht-aufzählbaren und/oder
Symbol-Eigenschaften ist eine WeakMap extern zum Objekt, und es gibt keine Möglichkeit für Benutzercode, die Metadaten durch Reflexionsmethoden wieObject.getOwnPropertySymbolsabzurufen. - Im Vergleich zu einerClosure kann dieselbe WeakMap für alle Instanzen, die von einem Konstruktor erstellt wurden, wiederverwendet werden. Das macht sie speichereffizienter und erlaubt verschiedenen Instanzen derselben Klasse, die privaten Mitglieder voneinander zu lesen.
let Thing;{ const privateScope = new WeakMap(); let counter = 0; Thing = function () { this.someProperty = "foo"; privateScope.set(this, { hidden: ++counter, }); }; Thing.prototype.showPublic = function () { return this.someProperty; }; Thing.prototype.showPrivate = function () { return privateScope.get(this).hidden; };}console.log(typeof privateScope);// "undefined"const thing = new Thing();console.log(thing);// Thing {someProperty: "foo"}thing.showPublic();// "foo"thing.showPrivate();// 1Dies entspricht grob dem Folgenden unter Verwendung vonprivaten Feldern:
class Thing { static #counter = 0; #hidden; constructor() { this.someProperty = "foo"; this.#hidden = ++Thing.#counter; } showPublic() { return this.someProperty; } showPrivate() { return this.#hidden; }}const thing = new Thing();console.log(thing);// Thing {someProperty: "foo"}thing.showPublic();// "foo"thing.showPrivate();// 1Zuordnen von Metadaten
EineWeakMap kann verwendet werden, um einem Objekt Metadaten zuzuordnen, ohne die Lebensdauer des Objekts selbst zu beeinflussen. Dies ist dem Beispiel der privaten Mitglieder sehr ähnlich, da private Mitglieder ebenfalls als externe Metadaten modelliert sind, die nicht an derprototypischen Vererbung teilnehmen.
Dieses Anwendungsbeispiel kann auf bereits erstellte Objekte erweitert werden. Zum Beispiel können wir im Web zusätzliche Daten mit einem DOM-Element verknüpfen, auf die das DOM-Element später zugreifen kann. Ein gängiger Ansatz ist, die Daten als Eigenschaft anzuhängen:
const buttons = document.querySelectorAll(".button");buttons.forEach((button) => { button.clicked = false; button.addEventListener("click", () => { button.clicked = true; const currentButtons = [...document.querySelectorAll(".button")]; if (currentButtons.every((button) => button.clicked)) { console.log("All buttons have been clicked!"); } });});Dieser Ansatz funktioniert, hat aber einige Tücken:
- Die
clicked-Eigenschaft ist aufzählbar und wird daher inObject.keys(button),for...in-Schleifen usw. angezeigt. Dies kann durch die Verwendung vonObject.defineProperty()gemildert werden, macht aber den Code ausführlicher. - Die
clicked-Eigenschaft ist eine normale String-Eigenschaft, sodass sie von anderem Code zugegriffen und überschrieben werden kann. Dies kann durch Verwendung einesSymbol-Schlüssels gemildert werden, aber der Schlüssel wäre immer noch überObject.getOwnPropertySymbols()zugänglich.
Die Verwendung einerWeakMap behebt diese Probleme:
const buttons = document.querySelectorAll(".button");const clicked = new WeakMap();buttons.forEach((button) => { clicked.set(button, false); button.addEventListener("click", () => { clicked.set(button, true); const currentButtons = [...document.querySelectorAll(".button")]; if (currentButtons.every((button) => clicked.get(button))) { console.log("All buttons have been clicked!"); } });});Hier kennt nur der Code, der aufclicked zugreift, den geklickten Zustand jedes Buttons, und externer Code kann die Zustände nicht ändern. Darüber hinaus werden, wenn einer der Buttons aus dem DOM entfernt wird, die zugehörigen Metadaten automatisch vom Garbage Collector gesammelt.
Caching
Sie können Objekte, die an eine Funktion übergeben werden, mit dem Ergebnis dieser Funktion verknüpfen, sodass dasselbe Objekt erneut übergeben wird und das zwischengespeicherte Ergebnis zurückgegeben werden kann, ohne die Funktion erneut auszuführen. Das ist nützlich, wenn die Funktion rein ist (d.h. sie verändert keine äußeren Objekte oder verursacht andere beobachtbare Nebeneffekte).
const cache = new WeakMap();function handleObjectValues(obj) { if (cache.has(obj)) { return cache.get(obj); } const result = Object.values(obj).map(heavyComputation); cache.set(obj, result); return result;}Dies funktioniert nur, wenn der Eingabewert Ihrer Funktion ein Objekt ist. Selbst wenn der Eingabewert nie erneut übergeben wird, bleibt das Ergebnis weiterhin im Cache, solange der Schlüssel (Eingabewert) existiert. Eine effektivere Methode ist die Verwendung einerMap in Verbindung mitWeakRef-Objekten, die es Ihnen ermöglichen, jeden Eingabewert-Typ mit seinem jeweiligen (potenziell großen) Berechnungsergebnis zu verknüpfen. Weitere Einzelheiten finden Sie im Beispiel zuWeakRefs und FinalizationRegistry.
Spezifikationen
| Specification |
|---|
| ECMAScript® 2026 Language Specification> # sec-weakmap-objects> |