Signatur
Beschreibung
Symbol.iterator ist eine statische, nicht beschreibbare Dateneigenschaft von Symbol und enthält eines der wohlbekannten Symbole (well-known symbols). Ein Objekt gilt als iterierbar, wenn es unter dem Schlüssel [Symbol.iterator] eine Methode besitzt, die ein Iterator-Objekt zurückgibt – also ein Objekt mit einer Methode next(), die Ergebnisse der Form { value, done } liefert.
Die Sprache ruft diese Methode automatisch auf bei for...of, dem Spread-Operator ([...obj], fn(...obj)), der Array-Destrukturierung, yield*, Array.from(), new Map(), new Set(), Promise.all() und weiteren Stellen. Eingebaute Typen wie Array, String, Map, Set, TypedArray, arguments sowie DOM-Sammlungen wie NodeList implementieren sie bereits.
Eigene Klassen oder Datenstrukturen (Listen, Bäume, Bereiche) macht man iterierbar, indem man [Symbol.iterator]() definiert – am bequemsten als Generator-Methode *[Symbol.iterator]() { … }. So lassen sich eigene Strukturen nahtlos mit allen Sprachmitteln für Iteration kombinieren.
Die Eigenschaft selbst hat die Attribute writable: false, enumerable: false, configurable: false; ihr Wert ist in jedem Realm (z. B. auch in iframes) dasselbe Symbol.
Rückgabewert
Symbol.iterator. Die damit hinterlegte Methode wird ohne Argumente aufgerufen und muss ein Iterator-Objekt (mit next()) zurückgeben.Beispiele
Eingebauten Iterator manuell verwenden
const farben = ['rot', 'grün'];
const it = farben[Symbol.iterator]();
console.log(it.next());
console.log(it.next());
console.log(it.next());
Eigene Klasse per Generator iterierbar machen
class Bereich {
constructor(start, ende, schritt = 1) {
this.start = start;
this.ende = ende;
this.schritt = schritt;
}
*[Symbol.iterator]() {
for (let i = this.start; i <= this.ende; i += this.schritt) {
yield i;
}
}
}
const r = new Bereich(1, 10, 3);
for (const n of r) console.log(n);
console.log([...r]);
const [erstes, zweites] = r;
console.log(erstes, zweites);
Iterator-Protokoll von Hand implementieren (verkettete Liste)
const liste = {
kopf: { wert: 'a', next: { wert: 'b', next: { wert: 'c', next: null } } },
[Symbol.iterator]() {
let knoten = this.kopf;
return {
next: () => {
if (!knoten) return { value: undefined, done: true };
const value = knoten.wert;
knoten = knoten.next;
return { value, done: false };
},
// Optional: wird bei break/return/throw aufgerufen
return: () => {
console.log('Aufräumen');
return { value: undefined, done: true };
}
};
}
};
for (const w of liste) {
console.log(w);
if (w === 'b') break;
}
Prüfen, ob ein Wert iterierbar ist
const istIterierbar = (wert) =>
wert != null && typeof wert[Symbol.iterator] === 'function';
console.log(istIterierbar([1, 2]));
console.log(istIterierbar('Text'));
console.log(istIterierbar(new Map()));
console.log(istIterierbar({ a: 1 }));
console.log(istIterierbar(42));
// Wichtig · Fallstricke
- Einfache Objekte sind nicht iterierbar:
for (const x of { a: 1 })wirftTypeError: … is not iterable. Für ObjekteObject.entries(),Object.keys()oderObject.values()verwenden. - Rückgabe muss ein Objekt sein: Liefert
[Symbol.iterator]()keinen Iterator bzw.next()kein Objekt, entsteht einTypeError. Eine normale Funktion ist nötig – keine Arrow-Funktion, wennthisauf die Instanz zeigen soll. - Einmal-Iteratoren: Generator-Objekte geben bei
[Symbol.iterator]()sich selbst zurück und sind nach einem Durchlauf erschöpft. Eine Klasse mit Generator-Methode liefert dagegen bei jedem Durchlauf einen frischen Iterator. - Überschreiben eingebauter Iteratoren (z. B.
Array.prototype[Symbol.iterator]) verändert das Verhalten von Spread undfor...ofglobal und kann Performance-Optimierungen der Engine aushebeln – vermeiden. - Unendliche Iteratoren sind erlaubt, dürfen aber nicht mit Spread oder
Array.from()vollständig ausgelesen werden (Endlosschleife). - Für asynchrone Datenquellen gibt es das Gegenstück
Symbol.asyncIteratormitfor await...of. - Kompatibilität: ES2015, in allen modernen Browsern und Node.js unterstützt; nicht im Internet Explorer.