JavaScript für ungeduldige Programmierer (ES2022-Ausgabe)
Bitte unterstützen Sie dieses Buch: kaufen Sie es oder spenden Sie
(Werbung, bitte nicht blockieren.)

22 Symbole



22.1 Symbole sind primitive Werte, die aber auch Objekt-ähnlich sind

Symbole sind primitive Werte, die über die Factory-Funktion Symbol() erzeugt werden.

const mySymbol = Symbol('mySymbol');

Der Parameter ist optional und liefert eine Beschreibung, die hauptsächlich für das Debugging nützlich ist.

22.1.1 Symbole sind primitive Werte

Symbole sind primitive Werte.

22.1.2 Symbole sind auch Objekt-ähnlich

Obwohl Symbole primitive Werte sind, sind sie auch Objekt-ähnlich, insofern als jeder durch Symbol() erzeugte Wert eindeutig ist und nicht nach Wert verglichen wird.

> Symbol() === Symbol()
false

Vor Symbolen waren Objekte die beste Wahl, wenn wir Werte benötigten, die eindeutig waren (nur mit sich selbst gleich).

const string1 = 'abc';
const string2 = 'abc';
assert.equal(
  string1 === string2, true); // not unique

const object1 = {};
const object2 = {};
assert.equal(
  object1 === object2, false); // unique

const symbol1 = Symbol();
const symbol2 = Symbol();
assert.equal(
  symbol1 === symbol2, false); // unique

22.2 Die Beschreibungen von Symbolen

Der Parameter, den wir der Symbol-Factory-Funktion übergeben, liefert eine Beschreibung für das erzeugte Symbol.

const mySymbol = Symbol('mySymbol');

Die Beschreibung kann auf zwei Arten abgerufen werden.

Erstens ist sie Teil des Strings, der von .toString() zurückgegeben wird.

assert.equal(mySymbol.toString(), 'Symbol(mySymbol)');

Zweitens können wir seit ES2019 die Beschreibung über die Eigenschaft .description abrufen.

assert.equal(mySymbol.description, 'mySymbol');

22.3 Anwendungsfälle für Symbole

Die Hauptanwendungsfälle für Symbole sind:

22.3.1 Symbole als Werte für Konstanten

Angenommen, Sie möchten Konstanten erstellen, die die Farben Rot, Orange, Gelb, Grün, Blau und Violett darstellen. Eine einfache Möglichkeit, dies zu tun, wäre die Verwendung von Strings.

const COLOR_BLUE = 'Blue';

Auf der positiven Seite erzeugt das Ausgeben dieser Konstante eine hilfreiche Ausgabe. Auf der negativen Seite besteht die Gefahr, einen nicht verwandten Wert mit einer Farbe zu verwechseln, da zwei Strings mit demselben Inhalt als gleich betrachtet werden.

const MOOD_BLUE = 'Blue';
assert.equal(COLOR_BLUE, MOOD_BLUE);

Dieses Problem können wir mit Symbolen lösen.

const COLOR_BLUE = Symbol('Blue');
const MOOD_BLUE = Symbol('Blue');

assert.notEqual(COLOR_BLUE, MOOD_BLUE);

Verwenden wir symbol-basierte Konstanten, um eine Funktion zu implementieren.

const COLOR_RED    = Symbol('Red');
const COLOR_ORANGE = Symbol('Orange');
const COLOR_YELLOW = Symbol('Yellow');
const COLOR_GREEN  = Symbol('Green');
const COLOR_BLUE   = Symbol('Blue');
const COLOR_VIOLET = Symbol('Violet');

function getComplement(color) {
  switch (color) {
    case COLOR_RED:
      return COLOR_GREEN;
    case COLOR_ORANGE:
      return COLOR_BLUE;
    case COLOR_YELLOW:
      return COLOR_VIOLET;
    case COLOR_GREEN:
      return COLOR_RED;
    case COLOR_BLUE:
      return COLOR_ORANGE;
    case COLOR_VIOLET:
      return COLOR_YELLOW;
    default:
      throw new Exception('Unknown color: '+color);
  }
}
assert.equal(getComplement(COLOR_YELLOW), COLOR_VIOLET);

22.3.2 Symbole als eindeutige Schlüsseln für Eigenschaften

Die Schlüsseln von Eigenschaften (Feldern) in Objekten werden auf zwei Ebenen verwendet.

Die Basis- und die Metaebene eines Programms müssen unabhängig sein: Basisebenen-Eigenschaftsschlüsseln sollten nicht mit Metaebenen-Eigenschaftsschlüsseln kollidieren.

Wenn wir Namen (Strings) als Eigenschaftsschlüsseln verwenden, stehen wir vor zwei Herausforderungen:

Hier sind zwei Beispiele, wo letzteres für JavaScript ein Problem war:

Symbole, die als Eigenschaftsschlüsseln verwendet werden, helfen uns hier: Jedes Symbol ist eindeutig und ein Symbol-Schlüssel kollidiert niemals mit einem anderen String- oder Symbol-Schlüssel.

22.3.2.1 Beispiel: Eine Bibliothek mit einer Metaebenen-Methode

Als Beispiel nehmen wir an, wir schreiben eine Bibliothek, die Objekte unterschiedlich behandelt, wenn sie eine spezielle Methode implementieren. So sieht die Definition eines Eigenschaftsschlüssels für eine solche Methode und deren Implementierung für ein Objekt aus:

const specialMethod = Symbol('specialMethod');
const obj = {
  _id: 'kf12oi',
  [specialMethod]() { // (A)
    return this._id;
  }
};
assert.equal(obj[specialMethod](), 'kf12oi');

Die eckigen Klammern in Zeile A ermöglichen es uns, anzugeben, dass die Methode den Schlüssel specialMethod haben muss. Weitere Details werden in §28.7.2 „Berechnete Schlüssel in Objekt-Literalen“ erläutert.

22.4 Öffentlich bekannte Symbole

Symbole, die innerhalb von ECMAScript spezielle Rollen spielen, werden als öffentlich bekannte Symbole bezeichnet. Beispiele hierfür sind:

  Übungen: Öffentlich bekannte Symbole

22.5 Konvertierung von Symbolen

Was passiert, wenn wir ein Symbol sym in einen anderen primitiven Typ konvertieren? Tab. 15 gibt die Antworten.

Tabelle 15: Die Ergebnisse der Konvertierung von Symbolen in andere primitive Typen.
Konvertieren nach Explizite Konvertierung Koerzition (implizite Konv.)
boolean Boolean(sym) OK !sym OK
number Number(sym) TypeError sym*2 TypeError
string String(sym) OK ''+sym TypeError
sym.toString() OK `${sym}` TypeError

Eine wichtige Fallstricke bei Symbolen ist, wie oft Ausnahmen geworfen werden, wenn sie in etwas anderes konvertiert werden. Was steckt dahinter? Erstens ist die Konvertierung in eine Zahl nie sinnvoll und sollte gewarnt werden. Zweitens ist die Konvertierung eines Symbols in einen String tatsächlich für die Diagnoseausgabe nützlich. Es ist jedoch auch sinnvoll, vor einer versehentlichen Umwandlung eines Symbols in einen String zu warnen (was eine andere Art von Eigenschaftsschlüssel ist).

const obj = {};
const sym = Symbol();
assert.throws(
  () => { obj['__'+sym+'__'] = true },
  { message: 'Cannot convert a Symbol value to a string' });

Der Nachteil ist, dass die Ausnahmen die Arbeit mit Symbolen komplizierter machen. Sie müssen Symbole explizit konvertieren, wenn Sie Strings über den Plus-Operator zusammenfügen.

> const mySymbol = Symbol('mySymbol');
> 'Symbol I used: ' + mySymbol
TypeError: Cannot convert a Symbol value to a string
> 'Symbol I used: ' + String(mySymbol)
'Symbol I used: Symbol(mySymbol)'

  Quiz

Siehe Quiz-App.