Signatur
Beschreibung
Map[Symbol.species] ist eine statische Accessor-Eigenschaft (nur Getter). Sie gibt den Wert von this zurück, also den Konstruktor, über den sie aufgerufen wird. Bei Map selbst ist das Map, bei einer Unterklasse class MyMap extends Map {} ist es MyMap.
Das Protokoll Symbol.species gibt es, damit eingebaute Methoden, die neue Instanzen desselben Typs erzeugen, bei Unterklassen den richtigen Konstruktor verwenden. Beispiele sind Array.prototype.map() oder Promise.prototype.then(). Unterklassen können den Getter überschreiben und so festlegen, welcher Konstruktor für abgeleitete Objekte verwendet wird.
Bei Map ist die Eigenschaft allerdings ungenutzt: Keine eingebaute Methode von Map.prototype erzeugt eine neue Map aus einer bestehenden. Auch das neuere Map.groupBy() fragt Symbol.species nicht ab. Die Eigenschaft ist hauptsächlich aus Gründen der Einheitlichkeit mit anderen eingebauten Klassen vorhanden. Eigene Methoden, die Kopien erzeugen, können sie aber bewusst auswerten.
Rückgabewert
this, also der Konstruktor, über den auf die Eigenschaft zugegriffen wird (z. B. Map oder eine Unterklasse).Beispiele
Standardwert bei Map und Unterklassen
class CacheMap extends Map {}
console.log(Map[Symbol.species] === Map);
console.log(CacheMap[Symbol.species] === CacheMap);
Species in einer eigenen Kopiermethode auswerten
class TrackedMap extends Map {
// Kopien sollen ganz normale Maps sein
static get [Symbol.species]() {
return Map;
}
filter(predicate) {
const Species = this.constructor[Symbol.species];
const result = new Species();
for (const [key, value] of this) {
if (predicate(value, key)) result.set(key, value);
}
return result;
}
}
const prices = new TrackedMap([["Apfel", 1.2], ["Mango", 2.5], ["Birne", 0.9]]);
const cheap = prices.filter((price) => price < 2);
console.log(cheap instanceof TrackedMap, cheap instanceof Map);
console.log([...cheap.keys()]);
Eingebaute Methoden ignorieren Species
class NoSpecies extends Map {
static get [Symbol.species]() {
throw new Error("wird nie aufgerufen");
}
}
const m = new NoSpecies([["a", 1]]);
// Keine Map-Methode fragt Symbol.species ab – es wird kein Fehler geworfen
m.set("b", 2);
m.forEach((v, k) => console.log(k, v));
console.log(Map.groupBy([1, 2, 3], (n) => (n % 2 ? "ungerade" : "gerade")));
// Wichtig · Fallstricke
- Keine Auswirkung auf eingebaute Methoden: Wenn du
Symbol.speciesin einer Map-Unterklasse überschreibst, ändert das am Verhalten vonMap.prototypenichts. Die Eigenschaft wirkt nur in eigenem Code, der sie ausdrücklich abfragt. - Die Eigenschaft hat nur einen Getter und keinen Setter. Eine Zuweisung wie
Map[Symbol.species] = Xwird im nicht-strikten Modus stillschweigend ignoriert und löst im Strict Mode einenTypeErroraus. Zum Überschreiben verwendest dustatic get [Symbol.species]()in einer Unterklasse oderObject.defineProperty. - Ein neuer Konstruktoraufruf wie
new MyMap(original)nutzt Species nicht. Er erzeugt immer eine Instanz der aufgerufenen Klasse. - Im Zusammenhang mit
Symbol.speciesgab es Sicherheits- und Performance-Bedenken. Es gibt deshalb TC39-Überlegungen, die Unterstützung einzuschränken. Neuer Code sollte sich nicht darauf verlassen. - Kompatibilität: Wird von allen modernen Browsern und von Node.js ab 6.5 unterstützt.