JavaScript/Schleife

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Schleifen sind ein Programmierkonstrukt und erfüllen den Zweck, eine Anweisung oder eine Gruppe von Anweisungen solange auszuführen, bis eine bestimmte Bedingung erfüllt ist, oder nicht mehr erfüllt ist. Die zu wiederholenden Anweisungen nennt man den Schleifenrumpf und die steuernde Bedingung demnach die Schleifenbedingung.

Eigentlich benötigt man in einer Programmiersprache gar keine eigenen Befehle, um Schleifen zu bilden. In der schlechten alten Zeit der unstrukturierten Programmierung verwendete man den goto Befehl, um das Programm an einer anderen Stelle fortzusetzen. Zusammen mit einer bedingten Anweisung genügte das, um im Programmablauf ein Stück zurück zu springen und so zu wiederholen. Das Problem dabei ist: das einzige, was einen Programmierer daran hindert, in seinem Programm kreuz und quer zu springen, so dass der Programmablauf nachher wie ein Topf Spaghetti aussieht, ist Disziplin und die Angst vor dem Schiffbruch, den man mit dieser Art der Programmsteuerung unweigerlich irgendwann erleidet.

Bekannte Informatiklehrer wie Edsger Dijkstra und Niklaus Wirth empfahlen deshalb, dass die Auswertung der Schleifenbedingung entweder vor der Ausführung des Anweisungsblocks erfolgen sollte - die kopfgesteuerte Schleife - oder nach dessen Ausführung - die fußgesteuerte Schleife. JavaScript bietet für diese beiden Schleifentypen eigene Syntax an: die while-Schleife für die Kopfsteuerung und die do...while Schleife für die Fußsteuerung.

Schleifen müssen oftmals in einer bestimmten Anzahl durchlaufen werden. Ebenso setzt man sie ein, um bestimmte Datenstrukturen wie Arrays oder Listen von Anfang bis zum Ende zu durchlaufen. Mit der for-Schleife, der for..in-Schleife und der for..of-Schleife bietet JavaScript hier zusätzlichen Komfort.

Und schließlich hat sich herausgestellt, dass es recht umständlichen Code ergeben kann, wenn man eine Schleifenbedingung wirklich nur vor oder nach dem Anweisungsblock notiert. Es passiert oft, dass man mitten im Schleifenrumpf feststellt, dass der Rest des Rumpfes nicht mehr durchlaufen werden muss und der nächste Durchlauf beginnen kann. Und es gibt viele Fälle, wo man feststellt, dass die Schleife abgebrochen werden kann. Man kann das gemäß der reinen Lehre mit bedingten Anweisungen und einer reinen kopf- oder fußgesteuerten Schleife lösen, was aber zu tief geschachtelten if-Abfragen führt und schwer lesbar ist. Auf diese Weise hat das goto doch noch seinen Weg nach JavaScript gefunden, in Form der Anweisungen continue und break.

Kopfgesteuerte Schleifen mit "while"

Die while-Schleife wertet zunächst die Schleifenbedingung aus. Ergibt sich dabei der Wert true (oder ein true-artiger Wert), wird der Schleifenrumpf ausgeführt. Danach beginnt der Ablauf von vorn.

Dieser Schleifentyp eignet sich

  • wenn Sie im Voraus nicht wissen, wie oft die Schleife zu durchlaufen ist
  • wenn es beabsichtigt ist, dass der Schleifenrumpf eventuell gar nicht durchlaufen wird

Syntax:

  while ( Bedingung )
  {
    Anweisungen
  }

Eine while-Schleife beginnt mit dem Schlüsselwort while (while = solange). Dahinter folgt, in runden Klammern ( und ) stehend, die Schleifenbedingung. Um eine Bedingung zu formulieren, brauchen Sie beispielsweise Vergleichsoperatoren oder logische Operatoren. Danach folgt der Schleifenrumpf, der eine einzelne JavaScript-Anweisung sein kann oder ein Block aus mehreren Anweisungen, die in geschweifte Klammern { und } gesetzt werden.

Empfehlung: JavaScript erlaubt, dass Sie die geschweiften Klammern um den Schleifenrumpf weglassen, wenn es sich nur um eine einzige Anweisung handelt. Tun Sie das nicht! Verwenden Sie die geschweiften Klammern konsequent. Sie verbessern die Lesbarkeit Ihres Programmes deutlich und falls bei einer Programmerweiterung eine zweite Anweisung hinzu kommt, sind die geschweiften Klammern schnell übersehen.

Schauen wir uns als Beispiel ein Stück Code an, das Leerstellen aus einer Eingabe entfernt. JavaScript bietet dazu zwar die Methode trim an, aber stellen wir uns vor, wir müssten den Internet Explorer 9 verwenden. Der kannte die noch nicht.

Schleife mit while ansehen …
let eingabe = document.getElementById("vorname").value;
let startPos = 0,
    endPos = eingabe.length - 1,
    entfernt, restlaenge, ergebnis;

while (startPos <= endPos && eingabe[startPos] == ' ') {
   startPos++;
}
if (endPos > startPos) {
   while (eingabe[endPos] == ' ') {
      endPos--;
   }
}
restlaenge = endPos+1 - startPos;
entfernt = eingabe.length - restlaenge;
ergebnis = eingabe.substr(startPos, restlaenge);

// Setze den Antwortsatz zusammen
let antwort = "Ich habe aus '" + eingabe + "' " + entfernt +
              " unnötige Leerstellen entfernt, '" + ergebnis + "'";

// Schreibe die Antwort in das p Element mit der id="ergebnis"
document.getElementById("ergebnis").textContent = antwort;

Das Beispiel gehört zu einer HTML Seite, auf der sich ein Eingabeelement mit der id "vorname" und ein Ausgabeelement mit der id "ergebnis" befinden, sowie einen Button, der den Programmcode aufruft. Klicken Sie auf "So sieht's aus" oder "ausprobieren", um sich das vollständige Beispiel anzuschauen.

Zweck des Beispiels ist, unnötigen Leerraum aus der Eingabe des Anwenders. Dazu prüft es mit Hilfe von zwei while-Schleifen, ob am Anfang oder am Ende der Eingabe Leerstellen zu finden sind. Dazu setzt es die Variablen startPos und endPos auf das erste und letzte Zeichen der Eingabe (Positionen in einer Zeichenkette beginnen in JavaScript mit 0 und nicht mit 1).

Die Bedingung der ersten while-Schleife besteht aus zwei Teilen, die durch &&, also UND, verknüpft sind. Teil 1 stellt sicher, dass die Schleife endet und nicht über das Ende der Eingabe hinaus läuft. endPos zeigt auf das letzte Zeichen der Eingabe, solange also startPos kleiner oder gleich endPos ist, ist alles gut. Teil 2 prüft, ob an der Position startPos eine Leerstelle steht. Ist eine von beiden Teilbedingungen falsch, ist das Ergebnis der UND-Verknüpfung falsch und der Schleifenrumpf wird nicht mehr ausgeführt. Hat der Anwender seinen Namen ohne Leerstelle am Anfang eingegeben, ist das sofort er Fall und startPos bleibt auf 0.

Die zweite while-Schleife funktioniert ähnlich, nur wird jetzt endPos > startPos vorab geprüft. Ist diese Bedingung falsch, so ist bereits die erste Schleife bis zum letzten Zeichen der Eingabe gelaufen und wir müssen nichts weiter tun. Andernfalls ist startPos kleiner als endPos und zeigt auf ein Zeichen, dass keine Leerstelle ist, so dass sichergestellt ist, dass die zweite Schleife ein Ende findet. Sie läuft so lange, wie endPos auf eine Leerstelle zeigt und setzt endPos solange zurück, bis das nicht mehr der Fall ist.

Beachten Sie: Wenn man Schleifen programmiert, muss man sicherstellen, dass die Schleife zu einem Ende kommen kann. Das ist nicht immer leicht, aber zwingend notwendig. Eine Schleife, die endlos läuft, können Sie in JavaScript nur dadurch beenden, dass Sie die Seite neu laden oder den Browser beenden. In unserem Beispiel wird dies bei Schleife 1 durch den Vergleich von startPos und endPos sichergestellt. Bei Schleife 2 haben wir durch theoretische Überlegungen nachgewiesen, dass sie ein Ende finden wird. Solche Überlegungen können auch gefährlich sein, denn sie gehen vom aktuellen Gesamtzustand des Programms aus. Eine Programmänderung an anderer Stelle kann dazu führen, dass die angestellten Überlegungen nicht mehr gültig sind. Eine defensivere Programmierung könnte in der zweiten Schleife den Vergleich endPos > startPos in die Schleifenbedingung aufgenehmen.

Nach den beiden Schleifen wird berechnet, wieviele Zeichen übrig bleiben und wieviele Leerstellen entfernt werdeb. Mit Hilfe der substr-Methode, über die Zeichenketten verfügen, werden diese Zeichen ab startPos aus der Eingabe herausgeholt und der Eingabewert damit überschrieben. Mit der alert-Funktion wird dem Anwender das Ergebnis mitgeteilt.

Anmerkung: Das Beispiel vereinfacht die Realität. Der Zeichensatz Ihres Computers verfügt über etliche Zeichen, die als Leerraum dargestellt werden und die hier eigentlich alle berücksichtigt werden müssten.

Fußgesteuerte Schleifen mit "do-while"

Die do-while-Schleife verhält sich ähnlich wie die while-Schleife, wertet aber die Schleifenbedingung erst nach Ausführung des Schleifenrumpfes aus. Dieser Schleifentyp eignet sich dann, wenn Sie mindestens einmal durch den Schleifenrumpf durchlaufen müssen, um entscheiden zu können, ob der Rumpf wiederholt werden muss oder nicht.

Syntax:

  do
  {
    Anweisungen
  }
  while ( Bedingung );

Eine do-while-Schleife beginnt mit dem Schlüsselwort do (do = tue etwas). Hinter dem do folgt eine einzelne Anweisung oder ein Anweisungsblock in geschweiften Klammern { und }. Darauf folgt als nächstes das Schlüsselwort while, hinter dem in runden Klammern ( und ) die Schleifenbedingung steht.

Der Aufbau der do-while-Schleife ist eigentlich seltsam. Zum einen ist das do technisch gar nicht nötig, es ist eher eine Lesehilfe für Sie, damit Sie nicht unvermittelt vor dem Beginn eines Anweisungsblocks stehen. Andererseits, wenn man es schon einmal hat, könnte man auch ganz auf die geschweiften Klammern verzichten. Das geht aber wie in jeder anderen Kontrollstruktur nur, wenn der Schleifenrumpf nur aus einer einzelnen Anweisung besteht. JavaScript orientiert sich hier schlicht an seinem Vorbild, der Programmiersprache C.

Beachten Sie: Hinter die schließende Klammer der Schleifenbedingung gehört ein Semikolon.

Als Beispiel für eine do-while Schleife schauen wir uns ein Script an, dass die Prioritäten der Anwenderin überprüft. Es bittet sie, die Abkürzung HTML zu erklären. Die Schleife endet, wenn sie richtig antwortet oder zum dritten Mal zeigt, dass sie den Sinn des Lebens nicht versteht.

do-while Schleife ansehen …
const richtigeAntwort = "how to make love";
let eingabe = "",
    nachricht = "",
    zaehler = 0;

do {
   zaehler++;
   eingabe = prompt(zaehler + ". Versuch: Was bedeutet 'HTML'?", "");
} while (eingabe != richtigeAntwort && zaehler < 3);

if (eingabe != richtigeAntwort) {
  nachricht = "Lernen Sie erst mal HTML! ...";
} 
else {
  nachricht = "Fein, Sie haben verstanden worum es geht...";
}

document.querySelector('output').innerText = nachricht;

Die do-while Schleife eignet sich für diese Situation, weil ohne eine Anwendereingabe nichts überprüft werden kann. Innerhalb der Schleife wird die Eingabe über ein prompt-Fenster erfasst und gezählt, wie oft schon etwas eingegeben wurde.

Die Schleifenbedingung bestehz aus zwei Teilbedingungen, die durch den UND-Operator && verknüpft sind. Die Schleifenbedingung muss true liefern, damit die Schleife wiederholt wird, deshalb ist die Teilbedingung 1, die Prüfung auf die richtige Eingabe, mit dem Ungleich-Operator != formuliert. Die Teilbedingung 2 prüft, ob die Anwenderin noch vor dem dritten Versuch ist. Nach dem dritten Durchlauf ist diese Teilbedingung falsch und die Schleife endet auf jeden Fall.

Wenn die Anwenderin die richtige Antwort im dritten Durchlauf eingibt, steht also nicht fest, aus welchen der beiden möglichen Ursachen die Schleife beendet wurde. Um das zu entscheiden, wird deshalb anschließend mit Hilfe einer if-Abfrage nochmals überprüft, ob die Eingabe falsch war, und je nachdem eine passende Antwortnachricht in der Variablen nachricht eingetragen. Diese wird dann mit document.querySelector('output').innerText in ein output-Element der Webseite eingefügt.

Es lohnt, an diesem Beispiel über eine verständliche Zählersteuerung nachzudenken. Der Zählerstand wird der Anwenderin angezeigt, muss also in den prompt-Fenstern von 1 bis 3 zählen. Und es ist für die Lesbarkeit des Programms sinnvoll, in der Schleifenbedingung zaehler < 3 abzufragen, bei einem Stand von 3 also abzubrechen. Das lässt sich dadurch erreichen, dass man die Variable auf 0 initialisiert und in der Schleife zunächst hochzählt. Man könnte statt dessen auch den Zähler vor der Schleife auf 1 initialisieren und ihn erst nach dem prompt, am Ende der Schleife, um 1 erhöhen. Aber dann müsste in der Schleifenbedingung zaehler <= 3 programmiert werden, was man so lesen könnte, dass nach dem dritten Durchlauf ein weiterer folgen soll. Deswegen wäre das die schlechtere Wahl.

Weitere Möglichkeiten, um Schleifen abzubrechen, werden weiter unten beschrieben.


Das nächste Beispiel soll noch einmal den Unterschied zwischen while und do-while verdeutlichen. Bei der normalen while-Schleife wird vor dem Ausführen des Codes die Schleifenbedingung überprüft, während bei der do-while-Schleife zuerst der Code ausgeführt und erst danach die Schleifenbedingung überprüft wird. Auf diese Weise können Sie erzwingen, dass Anweisungen innerhalb der Schleife auf jeden Fall mindestens einmal ausgeführt werden, auch wenn sich die Schleifenbedingung gleich am Anfang als unwahr herausstellt.

Beispiel ansehen …
 let x = 10;
 do {
   document.querySelector('output').innerHTML = "x × x = " + (x * x);
   x = x + 1;
 } while (x < 10);

Und einmal so:

Beispiel ansehen …
 let x = 10;
 while (x < 10) {
   document.querySelector('output').innerHTML = "x * x = " + (x * x);
   x = x + 1;
 }

In den Beispielen werden jeweils ein kleiner JavaScript-Bereich definiert. In beiden Bereichen wird eine Variable x definiert und mit dem Wert 10 vorbelegt.

Im ersten Bereich wird solange das Quadrat von x (das bei jedem Schleifendurchlauf um 1 erhöht wird) geschrieben, wie x kleiner als 10 ist. Da x ja schon am Beginn den Wert 10 hat, ist die Abbruchbedingung eigentlich schon von vorne herein erfüllt. Trotzdem wird einmal das Quadrat von x ausgegeben, da die Überprüfung der Schleifenbedingung erst nach dem Ausführen der Anweisungen innerhalb der Schleife erfolgt.

Im zweiten Script-Bereich herrschen die gleichen Bedingungen, jedoch wird dort eine normale while-Schleife notiert. Da x von vorne herein nicht kleiner als 10 ist, werden die Anweisungen der while-Schleife kein einziges Mal ausgeführt. Die Überprüfung der Schleifenbedingung, die am Anfang stattfindet, verhindert dies.

Schleifen mit "for"

Die for-Schleife wird verwendet, wenn eine bestimmte Anzahl an Durchläufen benötigt wird. Der Schleifenkopf fasst die Vorbereitung der ganzen Schleife, die Schleifenbedingung und die nötigen Berechnungen zur Fortsetzung der Schleife zusammen.

Syntax:

  for ( Initialisierung; Schleifenbedingung; Fortsetzung)
  {
    Anweisungen
  }

Sie notieren also zunächst das Schlüsselwort for und öffnen eine runde Klammer. Es folgen drei Code-Stücke, die die Schleife steuern und durch ein Semikolon voneinander getrennt werden. Danach notieren Sie eine schließende runde Klammer und es folgen eine oder mehrere Anweisungen, die durch geschweifte Klammern zusammengefasst werden.

Initialisierung
Sie geschieht als erstes, und nur ein einziges Mal. Hier können Sie beispielsweise einen Zähler auf seinen Anfangswert setzen.
Schleifenbedingung
Sie wird vor jedem Schleifendurchlauf ausgewertet und muss true liefern, damit der Schleifenrumpf ausgeführt wird. Sie können hier prüfen, ob der Endwert Ihres Zählers noch nicht erreicht ist.
Fortsetzung
Sie wird nach jedem Schleifendurchlauf ausgewertet. Ihr Zähler könnte hier um 1 erhöht werden.

Grundsätzlich handelt es sich bei der For-Schleife um eine Form von syntaktischem Zucker, denn man könnte, von einer Kleinigkeit abgesehen, auf die wir noch eingehen, die for-Schleife genauso gut als while-Schleife aufschreiben:

Ersatzschreibweise

  Initialisierung;
  while (Schleifenbedingung)
  {
    Anweisungen;
    Fortsetzung;
  }

Im häufigsten Fall wird die for-Schleife mit einer Zählervariablen gesteuert. Im Initialisierungsteil wird die Zählervariable auf einen Anfangswert gesetzt, in der Schleifenbedingung wird geprüft, ob der Endwert noch nicht erreicht ist und in der Fortsetzung wird die Zählervariable auf den nächsten Wert gesetzt (das kann eine Erhöhung um 1 sein, es kann aber auch jede andere sinnvolle Änderung stattfinden).

for-Schleife ansehen …
let htmlText = "";
for (let i = 10; i <= 36; i++) {
  htmlText = htmlText + '<span style="font-size:' + i + 'px">Schrift mit ' + i + ' Pixel Größe</span><br>';
}
document.querySelector('output').innerHTML = htmlText;

Das Beispiel definiert eine Stringvariable namens htmlText, in der im Verlauf einer for-Schleife eine Folge von <span> Elementen gesammelt wird. Am Ende wird das so entstandene HTML-Fragment als HTML-Inhalt in ein <output>-Element geschrieben, so dass der Browser es anzeigt.

Der Initialisierungsteil der for-Schleife lautet let i = 10. Hier wird also eine Variable i deklariert und auf den Wert 10 initialisiert. Die Schleifenbedingung ist i <= 36, die Schleife endet also, wenn i den Wert 36 überschreitet. Zur Fortsetzung wird die Variable i mit Hilfe des Inkrement Operators ++ um 1 erhöht (das Ergebnis des Operators ist irrelevant, man kann gleichermaßen Prä- oder Post-Inkrement verwenden).

Damit durchläuft i die Werte von 10 bis 36 und die Schleife erzeugt HTML für 27 <span> Elemente, in denen die Schriftgröße von 10x bis 36px variiert. Der erzeugte HTML Text wird mit Hilfe des + Operators zu einer langen Zeichenkette zusammengebaut. Zum Verständnis der zusammengesetzten Teile bei Ausgabe siehe auch . Danach ist die Schleife zu Ende.

An dieser Stelle findet sich der Unterschied von for und while. Dadurch, dass die Deklaration von i mit let innerhalb der for-Klammer stattfand, ist der Gültigkeitsbereich dieser Deklaration auf den Schleifenrumpf beschränkt. Sie können deshalb hinter der Schleife auf i nicht mehr zugreifen. Mit while können Sie diesen Effekt nicht erreichen. Wenn Sie nach der Schleife noch auf i zugreifen möchten, müssen Sie die Deklaration außerhalb der Schleife vornehmen.

Vorgezogene Deklaration von i
let htmlText = "";
let i;
for (i = 10; i <= 36; i++) {
  htmlText = htmlText + '<span style="font-size:' + i + 'px">Schrift mit ' + i + ' Pixel Größe</span><br>';
}
document.querySelector('output').innerHTML = htmlText;
console.log(i);   // Funktioniert.

Tatsächlich sind Sie bei der for-Schleife aber nicht darauf angewiesen, mit einer Zählervariablen zu arbeiten. Sie können jede Form von Initialisierung, Schleifenbedingung und Fortsetzung notieren, die Sie auch in einer while-Schleife verwenden. Beispielsweise könnten Sie eine verkettete Liste durchlaufen:

for-Schleife für eine verkettete Liste
for (let zeiger = liste; zeiger != null; zeiger = zeiger.next) {
  console.log("Listenelement hat den Wert " + zeiger.wert);
}

Enumerationsschleifen mit "for...in"

Eine spezielle Form der for-Schleife ist die for...in-Schleife. Sie dient nicht zur Konstruktion allgemeiner Schleifen, sondern dazu, alle Eigenschaften eines Objekts aufzulisten (zu „enumerieren“).

Syntax:

  for( Variable in Objekt )
  {
    Anweisungen
  }

JavaScript durchläuft mit einer solchen Schleife die Eigenschaften des Objekts, das hinter in angegeben ist, inklusive derjenigen, die über die Prototypenkette geerbt werden. Dabei werden aber nicht alle Eigenschaften verarbeitet. Eine Objekteigenschaft diese Bedingungen erfüllen, um von for...in erfasst zu werden:

  • Ihr Schlüssel muss ein String oder ein Arrayindex sein. Eigenschaften, deren Schlüssel ein Symbol ist, können zwar laut Propertydescriptor enumerierbar sein, werden von for...in aber übergangen. Arrayindexe werden aber nicht als Zahl, sondern ebenfalls als String geliefert.
  • Sie muss zum ersten Mal angetroffen werden. Diese Bedingung ist von Bedeutung, wenn for...in die Prototypkette verarbeitet. Enthält ein Prototypobjekt eine Eigenschaft, die unter gleichem Namen schon vorher angetroffen wurde, so wird sie von for...in nicht erneut aufgelistet.
  • Sie muss als aufzählbar deklariert sein, d.h. in ihrem Propertydescriptor muss die enumerable-Eigenschaft auf true gesetzt sein. Jedes Objekt erbt Eigenschaften von Object.prototype, aber weil diese nicht als aufzählbar deklariert sind, gibt eine for...in Schleife sie nicht aus.

Die Reihenfolge, in der for...in die Eigenschaften liefert, ist von der JavaScript-Spezifikation definiert. Falls ein Array durchlaufen wird, so werden zunächst alle Array-Indexe in numerisch aufsteigender Reihenfolge geliefert. Danach folgen die übrigen Eigenschaften, und zwar in der Reihenfolge in der sie am Objekt gespeichert wurden.

Beispiel ansehen …
 let Ausgabe = "";
 for (let Eigenschaft in document) {
   Ausgabe = Ausgabe + "document." + Eigenschaft + ": " + document[Eigenschaft] + "<br>";
 }

 document.querySelector('output').innerHTML = Ausgabe;

Das Beispiel zeigt, wie Sie mit Hilfe einer for...in-Schleife einiges über die JavaScript-Fähigkeiten Ihres Browsers herausbekommen können. Im Beispiel werden die Eigenschaften des Objektes document ausgegeben. Jeder Schleifendurchgang liefert den Namen einer Eigenschaft von document. Mit Hilfe von document[Eigenschaft], der Indexschreibweise für den Zugriff auf Objekteigenschaften, wird der Wert dieser Eigenschaft bestimmt. Name und Wert werden an die Variable Ausgabe angehängt. Sind alle Eigenschaften durchlaufen, endet die Schleife und die gefundenen Informationen werden als HTML Inhalt in ein <output>-Element geschrieben.

Beachten Sie: Wenn Sie for...in benutzen, um über ein Array zu iterieren (Array-Indizes sind Eigenschaften des Arrays), erhalten Sie nicht nur die Array-Inhalte. Die length-Eigenschaft des Arrays ist zwar nicht enumerierbar, aber die Methoden von Array.prototype sind es und </code>for...in liefert sie mit. Hier ist die <code>for...of-Schleife die geeignetere Wahl.

Iterationsschleifen mit "for...of"

Die for...of-Schleife dient dazu, alle Elemente eines iterierbaren Objekts zu durchlaufen. Das sind diejenigen Objekte, die einen Iterator bereitstellen. Dazu gehören zum Beispiel Arrays und Strings, aber auch Objekte des DOM wie NodeList. Im Artikel zu Iteratoren können Sie auch sehen, dass for...of tatsächlich nur Syntaktischer Zucker für die Verarbeitung eines Iterators ist.

Sie erkennen ein iterierbares Objekt daran, dass es eine Methode besitzt, deren Schlüssel (nicht Name!) in Symbol.iterator zu finden ist.

Iteratoren und die for...of Schleife sind ein Feature des JavaScript-Standards ECMAScript 6 (ECMAScript 2015) und deshalb in älteren Browsern wie dem Internet Explorer nicht zu finden.

Kompabilitätsinformationen bei kangax

Syntax:

  for( Variable of iterierbares Objekt )
  {
    Anweisungen
  }

Beispielsweise liefert der Iterator eines Arrays alle Arrayelemente von Index 0 bis zum Index (length-1). Damit können Sie die Werte eines Arrays bequem mit for...of durchlaufen - allerdings ohne dabei den Index zu kennen, den ein bestimmtes Element hat. Dafür müssten Sie eine for-Schleife programmieren oder die forEach-Methode des Arrays verwenden. Sie könnten auch einen eigenen Array-Iterator schreiben, der Index und Wert bereitstellt - im Abschnitt zu Generatoren finden Sie dafür ein Beispiel.

Vergleich der for...in- mit der for...of-Schleife
const numbers = [2, 3, 5, 7, 11, 13, 17, 19];
numbers.name = 'Primzahlen';

//for-in-Schleife
for (let i in numbers) {
  console.log(i);   // 0, 1, 2, 3, 4, 5, 6, 7, name
}

//for-of-Schleife
for (let i of numbers) {
  console.log(i);   // 2, 3, 5, 7, 11, 13, 17, 19
}

Sie sehen, dass Sie mit for...in die Namen der Objekteigenschaften erhalten - wozu im Falle eines Arrays auch die Arrayindizes gehören (die length-Eigenschaft eines Arrays ist als nicht aufzählbar gekennzeichnet und fehlt deshalb). Dagegen durchläuft for...of die Werte, die der in Arrays eingebauten Iterator liefert, das sind die Arrayinhalte.

Die for...of-Schleife eignet sich für Strings, Arrays und Maps, NodeLists und generell alle Objekte, die über einen Iterator verfügen.

Der Map-Iterator hat die Besonderheit, dass er Ihnen nicht nur den gespeicherten Wert liefert, sondern ein Array mit zwei Einträgen: Dem Schlüssel und dem Wert. Sie können mit Hilfe der Destrukturierung bequem darauf zugreifen.

for...of-Schleife über einer Map
let map = new Map();
map.set("self", "HTML");
map.set("world", "Wide Web");

for (let [key, value] of map) {
   console.log(`Schlüssel: ${key} - Wert: ${value}`);
}
Beachten Sie: Falls Sie versuchen, die for...of-Schleife auf ein Objekt anzuwenden, das keinen Iterator bereitstellt, wirft JavaScript einen TypeError.

Kontrolle innerhalb von Schleifen - break und continue

Schleifen sind kritische Faktoren innerhalb eines Scripts. Bei komplizierteren Aufgaben ist es manchmal nicht einfach, eine Schleife so zu programmieren, dass das Verlassen der Schleife über die normale Schleifenbedingung möglich ist. Das heißt nicht, dass das unmöglich ist, es führt dann aber zu aufwändigen Abfragen und dementsprechend tiefer Schachtelung von Logik innerhalb der Schleife. Die Möglichkeit, eine Schleife gezielt zu verlassen oder zumindest den aktuellen Durchlauf abzubrechen, kann das Programm leichter lesbar machen.

break

Mit der break-Anweisung können Sie eine Schleife sofort beenden. Dazu müssen Sie innerhalb des Schleifenkörpers das Wort break als Anweisung notieren. Natürlich ergibt das nur dann einen Sinn, wenn die break-Anweisung von einer Abfrage abhängt, andernfalls würde die Schleife immer im ersten Durchlauf abgebrochen.

Beispiel ansehen …
    let daten = [ 1, 3, 9, 0, 5, 2 ];
    let i, summe = 0;
    for (i=0; i < daten.length; i++) {
        summe = summe + daten[i];
        if (summe > 10) {
            break;
        }
        console.log(`i = ${i}, summe = ${summe}`);
    }
    console.log(`Grenzwert erreicht bei i=${i}, summe=${summe}`);

Im Beispiel werden die Elemente eines Arrays aufaddiert. Sobald die Summe den Wert 10 überschreitet, wird die Schleife abgebrochen. Ohne break müsste man die Summenbedingung zweimal prüfen: Einmal, um den Rest des Schleifenrumpfs zu überspringen, und dann noch einmal in der Schleifenbedingung.

continue

Die continue-Anweisung ist die kleine Schwester von break. Sie bricht nicht die gesamte Schleife ab, sondern nur den aktuellen Schleifendurchlauf. Durch continue wird die Verarbeitung der Anweisungen des Schleifenrumpfs abgebrochen. In einer mit while oder do...while gebildeten Schleife folgt nach continue die Prüfung der Schleifenbedingung und bei Erfolg der nächste Durchlauf. In einer mit for gebildeten Schleife wird vor dem Prüfen der Schleifenbedingung auch noch die erforderliche Logik zur Fortsetzung der Schleife ausgeführt.

Beispiel ansehen …
   let daten = [ 1, 3, 9, 0, 15, 2 ];
   let anzahl = 0,
       summe = 0;
   for (let wert of daten) {
      if (wert <= 5)
         continue;
      anzahl++;
      summe = summe + wert;
   }
   console_log(`Die Summe der ${anzahl} Werte über 5 ist ${summe}`);

Dieses Beispiel verwendet eine for..of-Schleife, um ein Array zu durchlaufen. Nur die Werte über 5 sollen berücksichtigt werden. Deshalb beginnt die Schleife mit einer Prüfung, ob der aktuelle Wert kleiner oder gleich 5 ist. Ist das der Fall, wird der Rest der Schleife mit continue übersprungen. Für Werte über 5 wird ein Zähler erhöht und der Wert aufaddiert.

Dieses Beispiel ist zu klein, um den Lesbarkeitsgewinn durch continue augenfällig zu machen. Natürlich könnte man die Bedingung auch herumdrehen (wert > 5) und die beiden Anweisungen zum Zählen und Addieren als Anweisungsblock der Bedingung unterordnen. Die Verwendung von continue wäre dann nicht nötig. Denken Sie aber an eine Schleife, wo es mehrere Fälle geben kann, die zum Überspringen des restlichen Schleifenrumpfes führen. Oder wo die Erkenntnis, dass der Rest des Rumpfes nicht ausgeführt werden darf, erst in einer tieferen Schachtelung von Bedingungen gewonnen wird. Ohne continue wird es dann deutlich komplizierter.

Es soll aber nicht verschwiegen werden, dass man durch eine Umstrukturierung des Programms oft auf continue verzichten und eventuell die Lesbarkeit sogar verbessern kann. Wenn in einer Schleife eine komplexe Logik nötig ist, um zu erkennen, ob die eigentliche Schleifenverarbeitung überhaupt ausgeführt werden darf, dann sollte man überlegen, ob man die Prüflogik nicht in eine Funktion auslagern sollte, die true zurückgibt, wenn die Verarbeitung zulässig ist:

Auslagern der Prüflogik
   let daten = [ 1, 3, 9, 0, 15, 2 ];
   let anzahl = 0,
       summe = 0;
   for (let wert of daten) {
      if (istVerarbeitbar(wert))
      {
         anzahl++;
         summe = summe + daten;
      }
   }
   console.log(`Die Summe der ${anzahl} Werte über 5 ist ${summe}`);
   
   function istVerarbeitbar(wert) {
      return wert > 5;
   }

Natürlich gilt auch hier die Einschränkung vom vorherigen Beispiel: es ist zu klein, um den Nutzen zu zeigen. Stellen Sie sich aber vor, dass istVerarbeitbar eine Funktion mit 20 Zeilen oder mehr wäre. In diesem Fall hilft es der Lesbarkeit deutlich, dass die eigentliche Schleife nicht von diesen Zeilen aufgeblasen wird.

break und continue mit Label

Programme, in denen mehrere Schleifen ineinander geschachtelt werden, kommen vor. Und es kann dann auch vorkommen, dass in einer der inneren Schleifen festgestellt wird, dass eine weiter außen liegende Schleife abgebrochen werden muss, oder zumindest der aktuelle Durchlauf zu beenden ist.

Ein weiteres Szenario ist, dass eine Schleife eine switch-Anweisung enthält. Innerhalb von switch ist das break-Statement erforderlich, um den ausgewählten Fall abzuschließen. Aber was, wenn im switch festgestellt wird, dass die Schleife verlassen werden muss? Das break beendet ja nur den switch!

Man kann solche Fälle mit Hilfe von Schaltervariablen lösen, die man in der inneren Stufe setzt und in der äußeren Stufe abfragt. Das macht den Code aber auch nicht besser verständlich. Als eine Alternative dazu bietet JavaScript die Möglichkeit, einer Schleifenanweisung (und auch switch) einen Namen geben. Solche Namen heißen traditionell Label, weil sie sozusagen ein Etikett an der Anweisung sind, mit dem man angeben kann: Ich beziehe mich auf diese Anweisung. Ein Label besteht aus einem JavaScript-Bezeichner, gefolgt von einem Doppelpunkt.

Der break- oder continue-Anweisung kann der Name des Labels als Argument hinzugefügt werden. Damit weiß JavaScript, was verlassen oder übersprungen werden soll.

Beachten Sie: Die JavaScript-Syntax verbietet einen Zeilenumbruch zwischen break beziehungsweise continue und dem Label.
Beispiel ansehen …
   let daten = [ 1, 3, 9, 0, 15, 2 ];
   let anzahl = 0,
       summe = 0;
summierung:
   for (let wert of daten) {
      switch(wert) {
      case 0:
         summe = summe * 2;
         break summierung;
      case 1:
         summe = summe + 1000;
         continue summierung;
      default:
         anzahl++;
         summe = summe + wert;
         break;
      }
   }
   console.log(`Die Summe der ${anzahl} Werte über 5 ist ${summe}`);

Die merkwürdige Logik dieses Programms sieht vor, dass der Wert 0 im Datenarray die bisherige Summe verdoppeln und dann die Summierung abbrechen soll. Der Wert 1 soll als 1000 summiert, aber nicht gezählt werden. Alle anderen Werte werden gezählt und aufaddiert.

Um die Schleife gezielt verlassen zu können, hat sie das Label summierung: bekommen. Die Steuerung erfolgt über eine switch-Anweisung.

  • Im case 0: Zweig wird mit break summierung; bewirkt, dass switch- und for-Anweidung verlassen werden.
  • Im case 1: Zweig fordert die Anweisung continue summierung;, dass der Rest des aktuellen Schleifendurchlaufs zu überspringen ist. Für die switch-Anweisung wirkt dieser continue wie ein break.
  • In allen anderen Fällen wird gezählt und addiert. Die dann folgende break-Anweisung steht ohne Angabe eines Labels und bezieht sich deshalb auf die Kontrollanweisung, der sie direkt untergeordnet ist: die switch-Anweisung.

Asynchrone Iteration mit for await ... of

Wenn Sie einen async-Generator iterieren möchten, erhalten Sie in jedem Iterationsschritt ein Promise, für das Sie entweder einen then-Handler installieren oder dessen Ergebnis Sie mit await erwarten müssen.

Die for await...of Schleife nimmt Ihnen diesen Schritt ab, und sie erkennt auch, ob der gelesene Iterator synchron oder asynchron ist. Sie müssen nicht unterscheiden, welche Form von Iterator Ihnen vorliegt.

Die for await...of Schleife dürfen Sie natürlich nur dort verwenden, wo ein await zulässig ist, also in einer async-Funktion oder auf globaler Ebene eines ECMAScript-Moduls.

Beispiel
function *getValues() {
   for (let i=0; i<10; i++)
      yield Promise.resolve(i);
}

for await (const value of getValues()) {
   console.log(value);
}

Quellen