Читать книгу JavaScript – Das Handbuch für die Praxis - David Flanagan - Страница 49
3.6Symbole
ОглавлениеSymbole wurden in ES6 eingeführt, um als Eigenschaftsnamen zu dienen, die nicht aus einem String bestehen. Dazu muss man wissen, dass der grundlegende Objekttyp von JavaScript eine ungeordnete Sammlung von Eigenschaften ist, wobei jede Eigenschaft einen Namen hat und einen Wert aufweist. Eigenschaftsnamen sind in der Regel (und waren bis ES6 ausschließlich) Zeichenketten. In ES6 und später können dafür jetzt aber auch Symbole verwendet werden:
let strname = "string name"; // Eine Zeichenkette als Eigenschaftsname.
let symname = Symbol("propname"); // Ein Symbol als Eigenschaftsname.
typeof strname // => "string": strname ist eine Zeichenkette.
typeof symname // => "symbol": symname ist ein Symbol.
let o = {}; // Ein neues Objekt erstellen.
o[strname] = 1; // Definieren einer Eigenschaft mit einem
// Namen, der aus einer Zeichenkette besteht.
o[symname] = 2; // Definieren einer Eigenschaft mit einem
// Namen, der aus einem Symbol besteht.
o[strname] // => 1: Zugriff auf die per Zeichenkette
// benannte Eigenschaft.
o[symname] // => 2: Zugriff auf die per Symbol benannte
// Eigenschaft.
Der Typ Symbol hat keine Literalsyntax. Um einen Symbolwert zu erhalten, rufen Sie die Funktion Symbol() auf. Sie gibt nie zweimal den gleichen Wert zurück, selbst wenn sie mit dem gleichen Argument aufgerufen wird. Wenn Sie mit Symbol() einen Symbolwert erzeugen, können Sie diesen Wert als Eigenschaftsnamen verwenden, um einem Objekt eine neue Eigenschaft hinzuzufügen, ohne sich Sorgen machen zu müssen, dass Sie eine vorhandene Eigenschaft mit demselben Namen überschreiben könnten. Und wenn Sie symbolische Eigenschaftsnamen verwenden und diese Symbole nicht gemeinsam mit anderen Codemodulen Ihres Programms nutzen, können Sie umgekehrt sicher sein, dass Ihre Eigenschaften nicht versehentlich von anderen Modulen überschrieben werden.
In der Praxis dienen Symbole als Mechanismus zur Spracherweiterung. Als in ES6 die for/of-Schleife (siehe 5.4.4) und iterierbare Objekte (siehe Kapitel 12) eingeführt wurden, musste eine Standardmethode festgelegt werden, durch deren Implementierung sich Klassen selbst iterierbar machen können. Hätte man dieser Iterationsmethode einen festen String-Namen gegeben, hätte dies zu Konflikten mit bestehendem Code führen können, sodass stattdessen ein symbolischer Name verwendet wurde. Wie wir in Kapitel 12 sehen werden, ist Symbol.iterator ein Symbolwert, der als Methodenname verwendet werden kann, um ein Objekt iterierbar zu machen.
Die Funktion Symbol() nimmt ein optionales String-Argument entgegen und gibt einen eindeutigen Symbolwert zurück. Wenn Sie ein String-Argument angeben, wird diese Zeichenkette in die Ausgabe der toString()-Methode des Symbols aufgenommen. Bitte beachten Sie jedoch, dass der zweimalige Aufruf von Symbol() mit der gleichen Zeichenfolge zwei völlig unterschiedliche Symbolwerte erzeugt.
let s = Symbol("sym_x");
s.toString() // => "Symbol(sym_x)"
toString() ist die einzige interessante Methode von Symbolinstanzen. Es gibt jedoch noch zwei weitere Funktionen, die sich auf Symbole beziehen und die Sie kennen sollten. Manchmal möchte man Symbole in eigenem Code nicht öffentlich verwenden, damit sichergestellt ist, dass darin definierte Eigenschaften niemals mit solchen Eigenschaften in Konflikt geraten, die in anderen Codeabschnitten verwendet werden. In anderen Situationen möchte man dagegen vielleicht einen Symbolwert definieren, um ihn großzügig mit anderem Code zu teilen. Das wäre z.B. der Fall, wenn Sie eine Art Erweiterung definieren, die auch von anderem Code genutzt werden kann, wie bei dem zuvor beschriebenen Symbol.iterator-Mechanismus.
Um das zu ermöglichen, gibt es in JavaScript eine globale Symbolregistrierung. Die Funktion Symbol.for() nimmt ein String-Argument entgegen und gibt einen Symbolwert zurück, der mit der übergebenen Zeichenfolge verknüpft ist. Wenn dieser Zeichenfolge noch kein Symbol zugeordnet ist, wird ein neues Symbol erstellt und zurückgegeben, andernfalls wird das bereits vorhandene Symbol zurückgeliefert. Die Funktion Symbol.for() unterscheidet sich also grundsätzlich von Symbol(): Symbol() gibt niemals den gleichen Wert zweimal zurück, Symbol.for() gibt dagegen immer den gleichen Wert zurück, wenn es mit der gleichen Zeichenkette aufgerufen wird. Die an Symbol.for() übergebene Zeichenkette erscheint in der Ausgabe von toString() für das zurückgegebene Symbol und kann auch durch den Aufruf von Symbol.keyFor() auf dem zurückgegebenen Symbol abgefragt werden.
let s = Symbol.for("shared");
let t = Symbol.for("shared");
s === t // => true
s.toString() // => "Symbol(shared)"
Symbol.keyFor(t) // => "shared"