Signatur
Beschreibung
Array[Symbol.species] ist ein statischer Getter auf dem Array-Konstruktor. Standardmäßig gibt er einfach this zurück, also den Konstruktor, auf dem er aufgerufen wird. Bei Array selbst ist das Array, bei einer Unterklasse wie class MyArray extends Array ist es MyArray.
Methoden, die ein neues Array erzeugen, ermitteln den Konstruktor über this.constructor[Symbol.species]. Das sind concat(), filter(), flat(), flatMap(), map(), slice() und splice() (für das Array mit den entfernten Elementen). Deshalb gibt myArray.map(...) bei einer Unterklasse automatisch wieder eine Instanz dieser Unterklasse zurück.
Wenn du Symbol.species in einer Unterklasse überschreibst, legst du fest, welcher Konstruktor für abgeleitete Ergebnisse verwendet wird. Typisch ist das, wenn die Unterklasse zusätzlichen Zustand oder Invarianten hat, die bei abgeleiteten Arrays nicht gelten sollen. Dann gibt man üblicherweise Array zurück.
Die neueren, nicht verändernden Methoden aus ES2023 (toSorted(), toReversed(), toSpliced(), with()) sowie Array.from() und Array.of() werten Symbol.species nicht aus. Die ES2023-Methoden geben immer ein normales Array zurück, Array.from() und Array.of() verwenden stattdessen this.
Rückgabewert
this, also der Konstruktor, auf dem der Getter aufgerufen wurde (z. B. Array oder eine Unterklasse). Unterklassen können einen anderen Konstruktor zurückgeben.Beispiele
Standardwert abfragen
console.log(Array[Symbol.species] === Array);
class MyArray extends Array {}
console.log(MyArray[Symbol.species] === MyArray);
Unterklasse: map() und filter() geben die Unterklasse zurück
class Liste extends Array {
summe() {
return this.reduce((a, b) => a + b, 0);
}
}
const zahlen = Liste.from([1, 2, 3, 4]);
const gerade = zahlen.filter((n) => n % 2 === 0);
console.log(gerade instanceof Liste);
console.log(gerade.summe());
Species überschreiben: abgeleitete Ergebnisse als normales Array
class SortierteListe extends Array {
// map() & Co. sollen normale Arrays liefern, weil die Sortierung danach nicht mehr garantiert ist
static get [Symbol.species]() {
return Array;
}
}
const liste = SortierteListe.of(1, 2, 3);
const verdoppelt = liste.map((n) => n * 2);
console.log(verdoppelt instanceof SortierteListe);
console.log(verdoppelt instanceof Array);
console.log(verdoppelt);
Methoden, die Species ignorieren
class MyArray extends Array {
static get [Symbol.species]() {
return Array;
}
}
const a = MyArray.of(3, 1, 2);
console.log(a.slice() instanceof MyArray); // nutzt species
console.log(a.toSorted() instanceof MyArray); // ignoriert species, immer Array
console.log(MyArray.from(a) instanceof MyArray); // nutzt this
// Wichtig · Fallstricke
- Nur ein Getter: Die Eigenschaft hat keinen Setter.
Array[Symbol.species] = Xhat keine Wirkung und wirft im Strict Mode einenTypeError. Zum Überschreiben definierst dustatic get [Symbol.species]()in einer Unterklasse, oder du verwendestObject.defineProperty. - Sicherheit und Robustheit: Weil eingebaute Methoden über
this.constructor[Symbol.species]beliebigen Code ausführen können, war species schon mehrfach Ursache von Sicherheitslücken in JS-Engines. Außerdem kostet es Performance. Es gibt Vorschläge, den Mechanismus einzuschränken. Neue Methoden (ES2023toSorted()usw.) verzichten deshalb bewusst darauf. Verlass dich in neuem Code möglichst nicht darauf. - Wenn
constructoroder[Symbol.species]den Wertundefinedodernullhat, wird ein normalesArrayerzeugt. Jeder andere Wert, der kein Konstruktor ist, führt zu einemTypeError. - Bei Arrays aus einem anderen Realm (z. B. aus einem iframe) wird das Array des aktuellen Realms verwendet.
- Kompatibilität: Wird von allen modernen Browsern und von Node.js 6.5+ unterstützt, nicht aber vom Internet Explorer.