W3docs

JavaScript Iterables und Iteratoren

Iterables sind ein grundlegendes Konzept in JavaScript. Dieser Leitfaden erklärt Iterables, Iteratoren und praktische Techniken für Sammlungen.

Einführung in JavaScript Iterables

JavaScript Iterables sind Objekte, die ein bestimmtes Protokoll implementieren und dadurch von Iterations-Konstrukten wie for...of verarbeitet werden können. Dieser Leitfaden behandelt die grundlegenden Konzepte, eingebaute Iterables und praktische Techniken für die Arbeit mit Sammlungen.

Was sind Iterables in JavaScript?

Im Kern ist ein Iterable ein Objekt, das die Methode Symbol.iterator implementiert und damit sequenziellen Zugriff auf seine Elemente ermöglicht. Mehrere eingebaute Typen in JavaScript sind iterierbar, darunter Array, String, Map, Set und weitere. Es ist wichtig, zwischen einem Iterable (dem Objekt, das durchlaufen wird) und einem Iterator (dem von Symbol.iterator zurückgegebenen Objekt, das den Durchlauf tatsächlich ausführt) zu unterscheiden. Diese Iterables sind integraler Bestandteil verschiedener Operationen wie Schleifen und Datenmanipulation.

Info

Für eine detaillierte Erkundung der JavaScript Map- und Set-Objekte verweisen wir auf unseren umfassenden Leitfaden zu JavaScript Map und Set.

Beispiel eines Iterable: Array

Schauen wir uns ein grundlegendes Beispiel eines Iterable in JavaScript an:

javascript— editable

Dieses Code-Snippet zeigt das Iterieren über ein array von Früchten, ein gängiges Iterable. Die for...of-Schleife ruft automatisch die Symbol.iterator-Methode des Iterable auf und verbraucht den resultierenden Iterator, bis done den Wert true hat. Siehe JavaScript-Schleifen für die vollständige Familie der Schleifenkonstrukte.

Warnung

Verwechseln Sie nicht for...of mit for...in. for...of iteriert über die Werte, die ein Iterable produziert (Array-Elemente, String-Zeichen, Map-Einträge). for...in iteriert über die aufzählbaren Eigenschaftsschlüssel eines Objekts – einschließlich geerbter – und ist für einfache Objekte gedacht, nicht für Arrays. Die Verwendung von for...in auf einem array liefert Index-Strings ("0", "1", …) und kann zusätzliche Eigenschaften erfassen. Bevorzugen Sie daher for...of für geordnete Daten.

Das Iterator-Protokoll

Das Herzstück eines Iterable ist seine Symbol.iterator-Methode. Das Protokoll ist ein präziser Vertrag:

  1. Ein Iterable hat eine Methode mit dem Schlüssel Symbol.iterator. Der Aufruf dieser Methode gibt einen Iterator zurück.
  2. Ein Iterator ist ein Objekt mit einer next()-Methode.
  3. Jeder Aufruf von next() gibt ein Objekt { value, done } zurück:
    • done: falsevalue ist das nächste Element in der Sequenz.
    • done: true — die Iteration ist abgeschlossen (value wird dann ignoriert oder trägt ein optionales Endergebnis).

Jedes Objekt, das diesen Vertrag einhält, funktioniert mit for...of, dem Spread-Operator, Destructuring und Array.from() – auch wenn Sie das Objekt selbst geschrieben haben. Symbol ist ein eingebauter eindeutiger Schlüssel; siehe Symbol-Typ, warum diese Protokollmethoden einen solchen statt eines einfachen String-Namens verwenden.

Beispiel: Ein benutzerdefiniertes range-Iterable

Ein klassischer Anwendungsfall ist ein Objekt, das einen numerischen Bereich lazy erzeugt, ohne jemals ein array aufzubauen:

javascript— editable

In diesem Beispiel gibt die Methode [Symbol.iterator]() jedes Mal einen neuen Iterator zurück, sodass dasselbe range-Objekt mehrfach durchlaufen werden kann. Die Methodenkurzschreibweise stellt sicher, dass this korrekt auf das range-Objekt verweist. (Die Verwendung einer Pfeilfunktion für [Symbol.iterator] würde this lexikalisch erfassen und das Muster kaputt machen.)

Generatoren: die einfache Methode zum Erstellen von Iterables

Das manuelle Schreiben von next() und das Verwalten des Zustands von Hand ist ausführlich. Eine Generatorfunktion – deklariert mit function* und unter Verwendung von yield – erzeugt automatisch einen Iterator. Jedes yield pausiert die Funktion und übergibt einen Wert an den Verbraucher; die Ausführung wird beim nächsten next()-Aufruf fortgesetzt. Dasselbe range wird damit erheblich kürzer:

javascript— editable

Das * vor dem Methodennamen macht sie zu einer Generatormethode, sodass range mit kaum Boilerplate iterierbar ist. Für alles, was Generatoren können – einschließlich bidirektionaler Kommunikation und Delegation mit yield* – siehe Generatoren und Async-Iteratoren und Generatoren.

Unendliche und lazy Sequenzen

Da ein Iterator den nächsten Wert nur auf Anfrage berechnet, kann er Sequenzen beschreiben, die zu groß – oder sogar unendlich – sind, um sie im Speicher zu halten. Das ist der Hauptgrund, einen benutzerdefinierten Iterator statt eines einfachen array zu schreiben:

javascript— editable
Warnung

Verwenden Sie niemals Spread ([...naturals()]) oder for...of ohne break bei einem unendlichen Iterable – das führt zu einer Endlosschleife. Ziehen Sie stattdessen eine begrenzte Anzahl von Werten mit next() heraus.

Iterables verarbeiten

Sobald ein Objekt iterierbar ist, öffnet sich die gesamte Sprache für es: Jedes Konstrukt, das ein Iterable akzeptiert, funktioniert mit Ihrem benutzerdefinierten Typ genauso wie mit Arrays.

Verwendung von Array.from()

Die Methode Array.from() erstellt ein neues array aus einem beliebigen Iterable- (oder array-ähnlichen) Objekt. Sie akzeptiert auch eine optionale Map-Funktion als zweites Argument, die auf jedes Element beim Aufbau des array angewendet wird – praktischer als Array.from(it).map(fn), da ein zweiter Durchlauf entfällt:

javascript— editable

Siehe JavaScript Map und Set für mehr zu Set, und Array-Methoden für das, was Sie tun können, sobald Sie ein array haben.

Spread-Syntax mit Iterables

Die Spread-Syntax (...) expandiert ein Iterable überall dort, wo Argumente oder Elemente erwartet werden – zum Zusammenführen von Arrays, Kopieren oder Übergeben von Elementen als Funktionsargumente:

javascript— editable

Für das vollständige Bild von ... in Spread- und Collect-Positionen, siehe Rest-Parameter und Spread-Syntax.

Destructuring und Rest

Destructuring-Zuweisung entnimmt Werte aus einem beliebigen Iterable nach Position, und das Rest-Muster (...) fasst den Rest in einem array zusammen:

javascript— editable

Mehr dazu erfahren Sie unter Destructuring-Zuweisung.

Das String-Iterable und Unicode-Sicherheit

Strings sind iterierbar, und entscheidend ist, dass der Iterator Unicode-Codepunkte durchläuft, nicht 16-Bit-Codeeinheiten. Das bedeutet, dass Surrogatpaar-Zeichen (Emoji, einige Schriften) intakt bleiben – im Gegensatz zur Indizierung mit [i] oder älteren for-Schleifen über .length, die sie aufteilen können:

javascript— editable

Wann immer Sie Zeichen, die dem Nutzer angezeigt werden, korrekt zählen oder aufteilen müssen, iterieren Sie den string (oder spreaden Sie ihn) statt sich auf .length zu verlassen. Weitere Informationen finden Sie im Kapitel Strings.

Zusammenfassung

  • Ein Objekt ist iterierbar, wenn es eine [Symbol.iterator]()-Methode hat, die einen Iterator zurückgibt – ein Objekt, dessen next() { value, done } liefert.
  • Verwenden Sie einen Generator (function* / yield) statt next() von Hand zu schreiben; das ist die kürzeste und fehlerunanfälligste Methode, etwas iterierbar zu machen.
  • Verwenden Sie for...of für die Werte geordneter/iterierbarer Daten; verwenden Sie for...in nur für die Schlüssel einfacher Objekte.
  • Einmal iterierbar, lässt sich Ihr Typ nahtlos mit Spread, Destructuring, Rest, Array.from(it, mapFn) und vielen eingebauten APIs verwenden.
  • Iteratoren sind lazy, sodass sie unendliche oder sehr große Sequenzen modellieren können, die in einem array niemals möglich wären.
  • Strings iterieren (nicht indizieren), um Unicode-Zeichen wie Emoji sicher zu verarbeiten.

Übungen

Übung
Welche der folgenden Aussagen über JavaScript Iterables sind korrekt?
Welche der folgenden Aussagen über JavaScript Iterables sind korrekt?
Was this page helpful?