Signatur
Beschreibung
Der Konstruktor Iterator() ist abstrakt. Ihr erzeugt mit ihm keine Iteratoren direkt, sondern leitet eigene Klassen mit class MeinIterator extends Iterator davon ab. Dadurch erben die Instanzen alle Methoden von Iterator.prototype, also die neuen Iterator-Helfer wie map(), filter(), take(), drop(), flatMap(), reduce(), toArray(), forEach(), some(), every() und find(). Außerdem erben sie [Symbol.iterator](), das this zurückgibt.
Die Unterklasse muss nur eine Methode next() bereitstellen. Diese gibt Objekte der Form { value, done } zurück. Danach funktioniert die Instanz mit for...of, mit dem Spread-Operator, mit Array.from() und mit allen Helfern. Die Helfer werten verzögert aus („lazy“) und eignen sich deshalb auch für unendliche Folgen.
Ruft ihr new Iterator() direkt auf, bekommt ihr einen TypeError. Das passiert, wenn new.target gleich Iterator selbst ist. Ohne new gibt es ebenfalls einen TypeError. Wollt ihr ein vorhandenes Iterator-ähnliches Objekt nur mit den Helfern ausstatten, ist meist Iterator.from() die einfachere Wahl.
Rückgabewert
super() aufgerufen wurde. Bei einem direkten Aufruf gibt es keinen Rückgabewert, sondern einen TypeError.Beispiele
Direkter Aufruf wirft TypeError
try {
const it = new Iterator();
} catch (err) {
console.log(err instanceof TypeError);
}
try {
Iterator(); // ohne new
} catch (err) {
console.log(err.name);
}
Eigene Countdown-Klasse mit Iterator-Helfern
class Countdown extends Iterator {
#current;
constructor(start) {
super(); // Pflicht in abgeleiteten Klassen
this.#current = start;
}
next() {
if (this.#current < 0) {
return { value: undefined, done: true };
}
return { value: this.#current--, done: false };
}
}
const werte = new Countdown(3)
.map((n) => n * 10)
.toArray();
console.log(werte);
console.log(new Countdown(1) instanceof Iterator);
Unendlicher Zahlenbereich mit for...of und Lazy-Auswertung
class Range extends Iterator {
#next;
#end;
#step;
constructor(start, end = Infinity, step = 1) {
super();
this.#next = start;
this.#end = end;
this.#step = step;
}
next() {
if (this.#next >= this.#end) {
return { value: undefined, done: true };
}
const value = this.#next;
this.#next += this.#step;
return { value, done: false };
}
}
// Endlicher Bereich (Ende exklusiv)
for (const n of new Range(1, 4)) {
console.log(n);
}
// Unendlicher Bereich: take() begrenzt die Auswertung
const geradeZahlen = new Range(1)
.filter((n) => n % 2 === 0)
.take(3)
.toArray();
console.log(geradeZahlen);
Alternative ohne Unterklasse: Iterator.from()
// Ein einfaches Objekt mit next(), aber ohne Iterator.prototype
let i = 0;
const roh = {
next: () => (i < 3 ? { value: i++, done: false } : { value: undefined, done: true })
};
const it = Iterator.from(roh);
console.log(it.map((x) => x + 1).toArray());
// Wichtig · Fallstricke
- Nicht direkt instanziierbar:
new Iterator()undIterator()werfen immer einenTypeError. Der Konstruktor ist nur übersuper()in einer Unterklasse nutzbar. next()ist Pflicht:Iterator.prototypebringt keine eigenenext()-Methode mit. Fehlt sie in der Unterklasse, schlagenfor...ofund alle Helfer mit einemTypeErrorfehl.- Aufräumen: Wenn euer Iterator Ressourcen hält (Dateien, Verbindungen), implementiert zusätzlich
return(). Diese Methode wird beibreakinfor...ofund von Helfern wietake()aufgerufen. - Generatoren sind oft einfacher: Generator-Objekte erben bereits von
Iterator.prototypeund haben die Helfer automatisch. Eine eigene Klasse lohnt sich vor allem bei komplexem Zustand oder zusätzlichen Methoden. - Kompatibilität: Der globale
Iteratorsamt Helfern ist erst seit 2024/2025 in allen großen Engines verfügbar (Chrome/Edge 122, Firefox 131, Safari 18.4, Node.js 22). In älteren Umgebungen istIteratornicht definiert (ReferenceError). Dort hilft ein Polyfill, etwacore-js. Iterator.prototype.constructorundIterator.prototype[Symbol.toStringTag]sind aus Gründen der Web-Kompatibilität als Accessor-Eigenschaften (Getter/Setter) umgesetzt, nicht als normale Dateneigenschaften.