Start · Sprachen · JavaScript · Referenz · Array[Symbol.species]

Array[Symbol.species]

Eigenschaft

Statische Getter-Eigenschaft, die den Konstruktor liefert, mit dem Array-Methoden wie <code>map()</code> oder <code>filter()</code> ihre neuen Arrays erzeugen.

seit JavaScript ES2015 Kategorie: method-property

Signatur

get Array[Symbol.species]

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

Typ
Function
Beschreibung
Der Wert von 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);
true true

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());
true 6

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);
false true [ 2, 4, 6 ]

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
false false true

// Wichtig · Fallstricke

  • Nur ein Getter: Die Eigenschaft hat keinen Setter. Array[Symbol.species] = X hat keine Wirkung und wirft im Strict Mode einen TypeError. Zum Überschreiben definierst du static get [Symbol.species]() in einer Unterklasse, oder du verwendest Object.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 (ES2023 toSorted() usw.) verzichten deshalb bewusst darauf. Verlass dich in neuem Code möglichst nicht darauf.
  • Wenn constructor oder [Symbol.species] den Wert undefined oder null hat, wird ein normales Array erzeugt. Jeder andere Wert, der kein Konstruktor ist, führt zu einem TypeError.
  • 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.