Signatur
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
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);
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);
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);
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);
// Wichtig · Fallstricke
- Getter statt Zuweisung: Definiere
[Symbol.species]alsstatic get. Eine einfache Zuweisung wieMyArray[Symbol.species] = Arrayschlägt stillschweigend fehl (im Strict Mode mitTypeError), weil die geerbte Eigenschaft nur einen Getter hat. Nutze bei BedarfObject.defineProperty(). - Nicht alle Methoden beachten Species: Statische Methoden wie
Array.from()undArray.of()verwendenthisund nichtSymbol.species. Die ES2023-MethodentoSorted(),toReversed(),toSpliced()undwith()liefern immer ein normalesArray.MapundSethaben 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 odernew 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.