Signatur
Beschreibung
Symbol ist ein eingebautes Objekt, dessen Konstruktor ein symbol-Primitive zurückgibt — auch als Symbol-Wert oder einfach Symbol bezeichnet —, das garantiert eindeutig ist. Symbols werden häufig verwendet, um einem Objekt eindeutige Eigenschaftsschlüssel hinzuzufügen, die nicht mit Schlüsseln kollidieren, die anderer Code dem Objekt hinzufügen könnte, und die vor Mechanismen verborgen bleiben, mit denen anderer Code typischerweise auf das Objekt zugreift. Das ermöglicht eine schwache Form der encapsulation bzw. eine schwache Form von information hiding.
Jeder Aufruf von Symbol() gibt garantiert ein eindeutiges Symbol zurück. Jeder Aufruf von Symbol.for("key") gibt für einen bestimmten Wert von "key" immer dasselbe Symbol zurück. Wird Symbol.for("key") aufgerufen und im globalen Symbol-Register existiert bereits ein Symbol mit dem gegebenen Schlüssel, wird dieses Symbol zurückgegeben. Andernfalls wird ein neues Symbol erstellt, unter dem gegebenen Schlüssel dem globalen Symbol-Register hinzugefügt und zurückgegeben.
Beschreibung
Um ein neues primitives Symbol zu erstellen, schreibt man Symbol() mit einem optionalen String als Beschreibung:
Der obige Code erstellt drei neue Symbols. Beachte, dass Symbol("foo") den String "foo" nicht in ein Symbol umwandelt. Es wird jedes Mal ein neues Symbol erzeugt.
Die folgende Syntax mit dem new-Operator wirft einen TypeError:
Dies verhindert, dass Entwickler ein explizites Symbol-Wrapper-Objekt statt eines neuen Symbol-Werts erstellen, und mag überraschend sein, da das Erstellen expliziter Wrapper-Objekte um primitive Datentypen im Allgemeinen möglich ist (beispielsweise new Boolean, new String und new Number).
Wenn du wirklich ein Symbol-Wrapper-Objekt erstellen möchtest, kannst du die Object()-Funktion verwenden.
Da Symbols der einzige primitive Datentyp mit Referenz-Identität sind (das heißt, dasselbe Symbol kann nicht zweimal erstellt werden), verhalten sie sich in gewisser Weise wie Objekte. Zum Beispiel sind sie garbage-collectable und können daher in WeakMap-, WeakSet-, WeakRef- und FinalizationRegistry-Objekten gespeichert werden.
Gemeinsam genutzte Symbols im globalen Symbol-Register
Die obige Syntax mit der Symbol()-Funktion erstellt ein Symbol, dessen Wert während der gesamten Lebensdauer des Programms eindeutig bleibt. Um Symbols zu erstellen, die dateiübergreifend und sogar realm-übergreifend (jedes hat seinen eigenen globalen Scope) verfügbar sind, verwendet man die Methoden Symbol.for() und Symbol.keyFor(), um Symbols im globalen Symbol-Register zu setzen und abzurufen.
Beachte, dass das „globale Symbol-Register" nur ein fiktives Konzept ist und keiner internen Datenstruktur in der JavaScript-Engine entsprechen muss — und selbst wenn ein solches Register existiert, ist sein Inhalt dem JavaScript-Code nicht zugänglich, außer über die Methoden for() und keyFor().
Die Methode Symbol.for(tokenString) nimmt einen String-Schlüssel entgegen und gibt einen Symbol-Wert aus dem Register zurück, während Symbol.keyFor(symbolValue) einen Symbol-Wert entgegennimmt und den zugehörigen String-Schlüssel zurückgibt. Beide sind zueinander invers, sodass Folgendes true ist:
Weil registrierte Symbols beliebig überall erstellt werden können, verhalten sie sich fast genauso wie die Strings, die sie umhüllen. Daher sind sie nicht garantiert eindeutig und nicht garbage-collectable. Deshalb sind registrierte Symbols in WeakMap-, WeakSet-, WeakRef- und FinalizationRegistry-Objekten nicht zulässig.
Well-known Symbols
Alle statischen Eigenschaften des Symbol-Konstruktors sind selbst Symbols, deren Werte über Realms hinweg konstant sind. Sie werden als Well-known Symbols bezeichnet und dienen als „Protokolle" für bestimmte eingebaute JavaScript-Operationen, sodass Nutzer das Verhalten der Sprache anpassen können. Wenn beispielsweise eine Konstruktorfunktion eine Methode mit Symbol.hasInstance als Namen besitzt, kodiert diese Methode ihr Verhalten für den instanceof-Operator.
Vor den Well-known Symbols nutzte JavaScript normale Eigenschaften, um bestimmte eingebaute Operationen zu implementieren. Beispielsweise versucht die JSON.stringify-Funktion, die toJSON()-Methode jedes Objekts aufzurufen, und die String-Funktion ruft die toString()- und valueOf()-Methoden des Objekts auf. Mit dem Hinzufügen weiterer Operationen kann die Kennzeichnung jeder Operation als „magic property" jedoch die Rückwärtskompatibilität brechen und das Verhalten der Sprache schwerer nachvollziehbar machen. Well-known Symbols erlauben es, die Anpassungen für normalen Code, der typischerweise nur String-Eigenschaften liest, „unsichtbar" zu halten.
Well-known Symbols kennen kein Konzept der garbage collectability, da sie in einer festen Menge vorliegen und über die gesamte Lebensdauer des Programms eindeutig sind, ähnlich intrinsischen Objekten wie Array.prototype. Daher sind sie auch in WeakMap-, WeakSet-, WeakRef- und FinalizationRegistry-Objekten zulässig.
Symbol-Eigenschaften an Objekten finden
Die Methode Object.getOwnPropertySymbols() gibt ein Array von Symbols zurück und ermöglicht es, Symbol-Eigenschaften eines gegebenen Objekts zu finden. Beachte, dass jedes Objekt ohne eigene Symbol-Eigenschaften initialisiert wird, sodass dieses Array leer ist, sofern du keine Symbol-Eigenschaften am Objekt gesetzt hast.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $description | string | undefined | Optionale lesbare Beschreibung des Symbols, die lediglich zur Debugging-Zwecken dient (sichtbar in Symbol.prototype.toString() und in der Konsole). Die Einzigartigkeit des Symbols wird davon nicht beeinflusst. |
Rückgabewert
symbol-Primitiv-Wert. Jeder Aufruf gibt einen anderen Wert zurück, selbst bei gleicher Beschreibung.Beispiele
typeof-Operator mit Symbols
typeof Symbol() === "symbol";
typeof Symbol("foo") === "symbol";
typeof Symbol.iterator === "symbol";
Symbols und for...in-Iteration
const obj = {};
obj[Symbol("a")] = "a";
obj[Symbol.for("b")] = "b";
obj["c"] = "c";
obj.d = "d";
for (const i in obj) {
console.log(i);
}
// "c" "d"
Symbol-Wrapper-Objekte als Eigenschaftsschlüssel
const sym = Symbol("foo");
const obj = { [sym]: 1 };
obj[sym]; // 1
obj[Object(sym)]; // still 1
Neue primitive Symbols erstellen
const sym1 = Symbol();
const sym2 = Symbol("foo");
const sym3 = Symbol("foo");
Kein neues Symbol pro Aufruf
Symbol("foo") === Symbol("foo"); // false
new Symbol() wirft TypeError
const sym = new Symbol(); // TypeError
Symbol-Wrapper-Objekt mit Object() erstellen
const sym = Symbol("foo");
typeof sym; // "symbol"
const symObj = Object(sym);
typeof symObj; // "object"
Invers-Beziehung von Symbol.for und Symbol.keyFor
Symbol.keyFor(Symbol.for("tokenString")) === "tokenString"; // true