Start · Sprachen · JavaScript · Referenz · Symbol.species

Symbol.species

Eigenschaft

Das bekannte Symbol <code>Symbol.species</code> bestimmt, welcher Konstruktor von eingebauten Methoden wie <code>map()</code> oder <code>then()</code> für abgeleitete Objekte verwendet wird.

seit JavaScript ES2015 Kategorie: method-property

Signatur

Symbol.species

Beschreibung

Symbol.species ist eine statische, nicht beschreibbare Dateneigenschaft von Symbol und enthält eines der sogenannten bekannten Symbole (well-known symbols). Klassen können damit eine statische Getter-Eigenschaft [Symbol.species] definieren. Diese gibt den Konstruktor zurück, mit dem eingebaute Methoden neue abgeleitete Objekte erzeugen.

Wenn du zum Beispiel von Array erbst, liefern Methoden wie map(), filter(), slice(), splice(), flat(), flatMap() und concat() standardmäßig Instanzen deiner Unterklasse zurück. Über static get [Symbol.species]() legst du fest, dass stattdessen z. B. ein normales Array entsteht. Das gleiche Prinzip gilt für Promise (then(), catch(), finally()), ArrayBuffer und SharedArrayBuffer (slice()), TypedArrays (map(), filter(), slice(), subarray()) sowie RegExp ([Symbol.split]()).

Die eingebauten Konstruktoren (Array, Map, Set, Promise, RegExp, ArrayBuffer, TypedArrays) besitzen bereits einen Getter [Symbol.species], der this zurückgibt. Deshalb „erben“ Unterklassen dieses Verhalten automatisch. Sinnvoll ist das Überschreiben vor allem dann, wenn deine Unterklasse zusätzlichen Zustand oder einen abweichenden Konstruktor hat, den die eingebauten Methoden nicht korrekt aufrufen können.

Rückgabewert

Typ
symbol
Beschreibung
Das bekannte Symbol Symbol.species. Die Eigenschaft ist nicht beschreibbar, nicht aufzählbar und nicht konfigurierbar.

Beispiele

Standardverhalten: Unterklasse bleibt erhalten

class MyArray extends Array {
  sum() {
    return this.reduce((a, b) => a + b, 0);
  }
}

const zahlen = MyArray.from([1, 2, 3, 4]);
const gerade = zahlen.filter((n) => n % 2 === 0);

console.log(gerade instanceof MyArray); // map/filter erzeugen MyArray
console.log(gerade.sum());
console.log(MyArray[Symbol.species] === MyArray);
true 6 true

Species überschreiben: abgeleitete Arrays als normales Array

class ReadonlyList extends Array {
  // Ergebnisse von map/filter sollen normale Arrays sein
  static get [Symbol.species]() {
    return Array;
  }
}

const liste = ReadonlyList.of('a', 'b', 'c');
const gross = liste.map((s) => s.toUpperCase());

console.log(liste instanceof ReadonlyList);
console.log(gross instanceof ReadonlyList);
console.log(gross instanceof Array, gross);
true false true [ 'A', 'B', 'C' ]

Promise-Unterklasse: then() liefert ein normales Promise

class TimeoutPromise extends Promise {
  static get [Symbol.species]() {
    return Promise;
  }
}

const p = new TimeoutPromise((resolve) => setTimeout(() => resolve(42), 10));
const weiter = p.then((wert) => wert * 2);

console.log(p instanceof TimeoutPromise);
console.log(weiter instanceof TimeoutPromise);
weiter.then(console.log);
true false 84

Species der eingebauten Konstruktoren abfragen

console.log(typeof Symbol.species);
console.log(Symbol.species.toString());
console.log(Array[Symbol.species] === Array);
console.log(Map[Symbol.species] === Map);
console.log(Promise[Symbol.species] === Promise);
symbol Symbol(Symbol.species) true true true

// Wichtig · Fallstricke

  • Getter statt Zuweisung: Definiere [Symbol.species] als static get. Eine einfache Zuweisung wie MyArray[Symbol.species] = Array schlägt stillschweigend fehl (im Strict Mode mit TypeError), weil die geerbte Eigenschaft nur einen Getter hat. Nutze bei Bedarf Object.defineProperty().
  • Nicht alle Methoden beachten Species: Statische Methoden wie Array.from() und Array.of() verwenden this und nicht Symbol.species. Die ES2023-Methoden toSorted(), toReversed(), toSpliced() und with() liefern immer ein normales Array. Map und Set haben zwar den Getter, nutzen ihn aber in keiner Methode.
  • Konstruktor-Signatur: Eingebaute Methoden rufen den Species-Konstruktor mit festen Argumenten auf, z. B. new C(length) bei Arrays oder new C(executor) bei Promises. Weicht der Konstruktor deiner Unterklasse davon ab, entstehen Fehler. Genau dann solltest du Species überschreiben.
  • Performance und Sicherheit: Der Species-Mechanismus macht Engines komplexer und war schon mehrfach Ursache von Sicherheitslücken. Es gibt Vorschläge, ihn teilweise zu entfernen. Verlasse dich deshalb nicht auf ungewöhnliche Species-Tricks.
  • Kompatibilität: Wird von allen modernen Browsern und von Node.js ab 6.5 unterstützt, aber nicht vom Internet Explorer.