Iteratoren und Generatoren in JavaScript
Iteratoren und Generatoren in JavaScript
~13 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Wir haben for...of seit Kapitel 7 auf Arrays angewendet, ohne zu fragen, WARUM das funktioniert. Dieses Kapitel beantwortet das – und zeigt, wie wir unserem eigenen Haushaltsbuch-Kontentyp dieselbe Fähigkeit verleihen.
Das Iterator-Protokoll
for...of funktioniert auf JEDEM Wert, der das "iterable"-Protokoll erfüllt: ein Objekt mit einer Methode unter dem speziellen Schlüssel Symbol.iterator, die einen ITERATOR zurückgibt – ein Objekt mit einer next()-Methode, die bei jedem Aufruf { value, done } liefert.
const betraege = [100, 200, 300];
const iterator = betraege[Symbol.iterator]();
console.log(iterator.next()); // { value: 100, done: false }
console.log(iterator.next()); // { value: 200, done: false }
console.log(iterator.next()); // { value: 300, done: false }
console.log(iterator.next()); // { value: undefined, done: true }GENAU das passiert intern bei JEDEM for...of-Durchlauf – die Schleife ruft wiederholt next() auf, bis done: true zurückkommt. Arrays, Strings, Map und Set implementieren dieses Protokoll bereits von Haus aus.
Generator-Funktionen: Iteratoren einfach bauen
Ein Iterator von Hand zu schreiben (wie oben) ist umständlich. Generator-Funktionen (erkennbar am function*) übernehmen das Verwalten von { value, done } automatisch – yield "pausiert" die Funktion und gibt einen Wert aus, bis next() erneut aufgerufen wird:
function* zaehleMonate(von, bis) {
for (let monat = von; monat <= bis; monat++) {
yield monat;
}
}
for (const monat of zaehleMonate(1, 3)) {
console.log(monat); // 1, dann 2, dann 3
}
// Auch manuell steuerbar, wie ein normaler Iterator:
const gen = zaehleMonate(1, 3);
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }Praxisbeispiel: eine eigene iterierbare Konten-Sammlung
Damit statten wir eine Sammlung von Konten mit Symbol.iterator aus – so lässt sich direkt for...of darauf anwenden, statt eine separate getAlleKonten()-Methode zu brauchen:
export class Kontensammlung {
#konten = [];
hinzufuegen(konto) {
this.#konten.push(konto);
}
*[Symbol.iterator]() { // Generator-Methoden-Shorthand innerhalb einer Klasse
for (const konto of this.#konten) {
yield konto;
}
}
}import { Kontensammlung } from './kontensammlung.js';
const sammlung = new Kontensammlung();
sammlung.hinzufuegen(new Konto('Girokonto', 1500));
sammlung.hinzufuegen(new Sparkonto('Tagesgeld', 5000, 2.5));
for (const konto of sammlung) { // funktioniert DANK Symbol.iterator, wie bei einem Array!
console.log(konto.formatiereKontostand());
}Tipp: Auch der Spread-Operator ([...sammlung]) und Array-Destructuring funktionieren automatisch mit JEDEM iterierbaren Objekt – ein weiterer Vorteil, das Protokoll zu implementieren, statt nur eine Getter-Methode anzubieten.
Generatoren für (theoretisch) unendliche Sequenzen
Ein Vorteil von Generatoren gegenüber Arrays: Werte werden erst bei Bedarf erzeugt ("lazy"), sodass sich auch unendliche Sequenzen modellieren lassen, ohne Speicher zu sprengen:
function* monatsZyklus() {
const monate = ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'];
let index = 0;
while (true) { // läuft unendlich weiter, ist aber unproblematisch dank 'lazy' Auswertung
yield monate[index % 12];
index++;
}
}
const zyklus = monatsZyklus();
console.log(zyklus.next().value); // 'Jan'
console.log(zyklus.next().value); // 'Feb'
// ... würde nach 'Dez' einfach wieder bei 'Jan' weitermachen