Wir müssen oft Aktionen wiederholen.
Zum Beispiel, Waren nacheinander aus einer Liste auszugeben oder einfach für jede Zahl von 1 bis 10 den gleichen Code auszuführen.
Schleifen sind eine Möglichkeit, denselben Code mehrmals zu wiederholen.
Die for…of- und for…in-Schleifen
Eine kleine Ankündigung für fortgeschrittene Leser.
Dieser Artikel behandelt nur grundlegende Schleifen: while
, do..while
und for(..;..;..)
.
Wenn Sie auf der Suche nach anderen Arten von Schleifen zu diesem Artikel gekommen sind, finden Sie hier die Hinweise:
Siehe for…in, um Objekteigenschaften zu durchlaufen.
Siehe for…of und iterables zum Durchlaufen von Arrays und iterierbaren Objekten.
Ansonsten lesen Sie bitte weiter.
Die while
Schleife hat die folgende Syntax:
while (Bedingung) { // Code // sogenannter „Loop Body“ }
Solange die condition
wahr ist, wird der code
aus dem Schleifenkörper ausgeführt.
Die folgende Schleife gibt beispielsweise i
aus, während i < 3
:
sei i = 0; while (i < 3) { // zeigt 0, dann 1, dann 2 alarm( i ); i++; }
Eine einzelne Ausführung des Schleifenkörpers wird als Iteration bezeichnet. Die Schleife im obigen Beispiel führt drei Iterationen durch.
Wenn i++
im obigen Beispiel fehlen würde, würde sich die Schleife (theoretisch) für immer wiederholen. In der Praxis bietet der Browser Möglichkeiten, solche Schleifen zu stoppen, und in serverseitigem JavaScript können wir den Prozess abbrechen.
Jeder Ausdruck oder jede Variable kann eine Schleifenbedingung sein, nicht nur Vergleiche: Die Bedingung wird von while
ausgewertet und in einen booleschen Wert konvertiert.
Eine kürzere Schreibweise für while (i != 0)
ist beispielsweise while (i)
:
sei i = 3; while (i) { // wenn i 0 wird, wird die Bedingung falsch und die Schleife stoppt alarm( i ); ich--; }
Für einen einzeiligen Körper sind keine geschweiften Klammern erforderlich
Wenn der Schleifenkörper eine einzelne Anweisung enthält, können wir die geschweiften Klammern {…}
weglassen:
sei i = 3; while (i) alarm(i--);
Die Bedingungsprüfung kann mithilfe der do..while
-Syntax unter den Schleifenkörper verschoben werden:
Tun { // Schleifenkörper } while (Bedingung);
Die Schleife führt zuerst den Körper aus, überprüft dann die Bedingung und führt sie, solange sie wahr ist, immer wieder aus.
Zum Beispiel:
sei i = 0; Tun { alarm( i ); i++; } while (i < 3);
Diese Syntaxform sollte nur verwendet werden, wenn der Schleifenkörper unabhängig davon, ob die Bedingung wahr ist, mindestens einmal ausgeführt werden soll. Normalerweise wird die andere Form bevorzugt: while(…) {…}
.
Die for
Schleife ist komplexer, aber auch die am häufigsten verwendete Schleife.
Es sieht so aus:
for (Anfang; Bedingung; Schritt) { // ... Schleifenkörper ... }
Lassen Sie uns die Bedeutung dieser Teile anhand eines Beispiels lernen. Die folgende Schleife führt alert(i)
für i
von 0
bis (jedoch nicht einschließlich) 3
aus:
for (let i = 0; i < 3; i++) { // zeigt 0, dann 1, dann 2 alarm(i); }
Sehen wir uns die for
Anweisung Schritt für Schritt an:
Teil | ||
---|---|---|
beginnen | let i = 0 | Wird beim Eintritt in die Schleife einmal ausgeführt. |
Zustand | i < 3 | Wird vor jeder Schleifeniteration überprüft. Bei „false“ stoppt die Schleife. |
Körper | alert(i) | Läuft immer wieder, solange die Bedingung wahr ist. |
Schritt | i++ | Wird bei jeder Iteration nach dem Text ausgeführt. |
Der allgemeine Schleifenalgorithmus funktioniert folgendermaßen:
Lauf beginnen → (wenn Bedingung → Körper ausführen und Schritt ausführen) → (wenn Bedingung → Körper ausführen und Schritt ausführen) → (wenn Bedingung → Körper ausführen und Schritt ausführen) → ...
Das heißt, begin
wird einmal ausgeführt und dann iteriert: Nach jedem condition
werden body
und step
ausgeführt.
Wenn Sie mit Schleifen noch nicht vertraut sind, könnte es hilfreich sein, zum Beispiel zurückzukehren und den Ablauf Schritt für Schritt auf einem Blatt Papier zu reproduzieren.
Genau das passiert in unserem Fall:
// for (let i = 0; i < 3; i++) alarm(i) // run begin sei i = 0 // wenn Bedingung → Hauptteil ausführen und Schritt ausführen if (i < 3) { alarm(i); i++ } // wenn Bedingung → Hauptteil ausführen und Schritt ausführen if (i < 3) { alarm(i); i++ } // wenn Bedingung → Hauptteil ausführen und Schritt ausführen if (i < 3) { alarm(i); i++ } // ...fertig, denn jetzt i == 3
Inline-Variablendeklaration
Hier wird die „Zähler“-Variable i
direkt in der Schleife deklariert. Dies wird als „Inline“-Variablendeklaration bezeichnet. Solche Variablen sind nur innerhalb der Schleife sichtbar.
for (sei i = 0; i < 3; i++) { alarm(i); // 0, 1, 2 } alarm(i); // Fehler, keine solche Variable
Anstatt eine Variable zu definieren, könnten wir eine vorhandene verwenden:
sei i = 0; for (i = 0; i < 3; i++) { // eine vorhandene Variable verwenden alarm(i); // 0, 1, 2 } alarm(i); // 3, sichtbar, da außerhalb der Schleife deklariert
Jeder Teil von for
kann übersprungen werden.
Beispielsweise können wir begin
weglassen, wenn wir am Schleifenanfang nichts tun müssen.
Wie hier:
sei i = 0; // wir haben es bereits deklariert und zugewiesen for (; i < 3; i++) { // kein Bedarf für „begin“ alarm( i ); // 0, 1, 2 }
Wir können den step
auch entfernen:
sei i = 0; für (; i < 3;) { alarm( i++ ); }
Dadurch ist die Schleife identisch mit while (i < 3)
.
Wir können tatsächlich alles entfernen und so eine Endlosschleife erzeugen:
für (;;) { // wiederholt sich ohne Grenzen }
Bitte beachten Sie, dass die beiden for
;
muss vorhanden sein. Andernfalls würde es zu einem Syntaxfehler kommen.
Normalerweise wird eine Schleife beendet, wenn ihre Bedingung falsch wird.
Mit der speziellen break
-Direktive können wir den Exit jedoch jederzeit erzwingen.
Die folgende Schleife fragt den Benutzer beispielsweise nach einer Reihe von Zahlen und wird unterbrochen, wenn keine Zahl eingegeben wird:
sei Summe = 0; while (wahr) { let value = +prompt("Geben Sie eine Zahl ein", ''); if (!value) break; // (*) Summe += Wert; } Alert( 'Summe: ' + Summe );
Die break
-Direktive wird in der Zeile (*)
aktiviert, wenn der Benutzer eine leere Zeile eingibt oder die Eingabe abbricht. Es stoppt die Schleife sofort und übergibt die Kontrolle an die erste Zeile nach der Schleife. Nämlich alert
.
Die Kombination „Endlosschleife + break
nach Bedarf“ eignet sich hervorragend für Situationen, in denen der Zustand einer Schleife nicht am Anfang oder Ende der Schleife, sondern in der Mitte oder sogar an mehreren Stellen ihres Körpers überprüft werden muss.
Die continue
Direktive ist eine „leichtere Version“ von break
. Es stoppt nicht die ganze Schleife. Stattdessen stoppt es die aktuelle Iteration und zwingt die Schleife, eine neue zu starten (sofern die Bedingung dies zulässt).
Wir können es verwenden, wenn wir mit der aktuellen Iteration fertig sind und mit der nächsten fortfahren möchten.
Die folgende Schleife verwendet continue
, um nur ungerade Werte auszugeben:
for (sei i = 0; i < 10; i++) { // wenn wahr, den verbleibenden Teil des Körpers überspringen if (i % 2 == 0) continue; alarm(i); // 1, dann 3, 5, 7, 9 }
Für gerade Werte von i
stoppt die continue
Direktive die Ausführung des Hauptteils und übergibt die Kontrolle an die nächste Iteration von for
(mit der nächsten Zahl). Der alert
wird also nur bei ungeraden Werten ausgelöst.
Die continue
Direktive trägt dazu bei, die Verschachtelung zu verringern
Eine Schleife, die ungerade Werte anzeigt, könnte so aussehen:
for (sei i = 0; i < 10; i++) { if (i % 2) { alarm( i ); } }
Aus technischer Sicht ist dies identisch mit dem obigen Beispiel. Sicherlich können wir den Code einfach in einen if
Block einschließen, anstatt continue
zu verwenden.
Aber als Nebeneffekt entstand dadurch eine weitere Verschachtelungsebene (der alert
innerhalb der geschweiften Klammern). Wenn der Code in if
länger als ein paar Zeilen ist, kann dies die allgemeine Lesbarkeit beeinträchtigen.
Kein break/continue
zur rechten Seite von „?“
Bitte beachten Sie, dass Syntaxkonstrukte, die keine Ausdrücke sind, nicht mit dem ternären Operator verwendet werden können ?
. Insbesondere sind Anweisungen wie break/continue
dort nicht erlaubt.
Nehmen wir zum Beispiel diesen Code:
wenn (i > 5) { alarm(i); } anders { weitermachen; }
…und schreiben Sie es mit einem Fragezeichen um:
(i > 5)? alarm(i): fortfahren; // Weiter ist hier nicht erlaubt
…es funktioniert nicht mehr: Es liegt ein Syntaxfehler vor.
Dies ist nur ein weiterer Grund, den Fragezeichenoperator nicht zu verwenden ?
statt if
.
Manchmal müssen wir aus mehreren verschachtelten Schleifen gleichzeitig ausbrechen.
Im folgenden Code durchlaufen wir beispielsweise i
und j
und fordern die Koordinaten (i, j)
von (0,0)
bis (2,2)
an:
for (sei i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { let input = prompt(`Value at coords (${i},${j})`, ''); // Was ist, wenn wir von hier aus zu „Fertig“ (unten) wechseln möchten? } } alarm('Fertig!');
Wir brauchen eine Möglichkeit, den Prozess zu stoppen, wenn der Benutzer die Eingabe abbricht.
Die normale break
nach input
würde nur die innere Schleife unterbrechen. Das reicht nicht – Etiketten, kommen Sie zur Rettung!
Ein Label ist ein Bezeichner mit einem Doppelpunkt vor einer Schleife:
labelName: für (...) { ... }
Die break <labelName>
-Anweisung in der Schleife unten führt zum Label aus:
außen: for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { let input = prompt(`Value at coords (${i},${j})`, ''); // wenn eine leere Zeichenfolge oder abgebrochen, dann brechen Sie beide Schleifen ab if (!input) break äußere; // (*) // etwas mit dem Wert machen... } } alarm('Fertig!');
Im obigen Code sucht break outer
nach oben nach der Bezeichnung outer
und bricht aus dieser Schleife aus.
Die Steuerung geht also direkt von (*)
zu alert('Done!')
.
Wir können die Beschriftung auch in eine separate Zeile verschieben:
äußere: for (sei i = 0; i < 3; i++) { ... }
Die continue
Direktive kann auch mit einem Label verwendet werden. In diesem Fall springt die Codeausführung zur nächsten Iteration der gekennzeichneten Schleife.
Etiketten erlauben es nicht, irgendwohin zu „springen“.
Labels erlauben es uns nicht, an eine beliebige Stelle im Code zu springen.
Dies ist zum Beispiel nicht möglich:
Bruchetikett; // zum Label unten springen (funktioniert nicht) Etikett: für (...)
Eine break
Direktive muss sich innerhalb eines Codeblocks befinden. Technisch gesehen reicht jeder beschriftete Codeblock aus, z. B.:
Etikett: { // ... Bruchetikett; // funktioniert // ... }
… Allerdings werden 99,9 % der break
innerhalb von Schleifen verwendet, wie wir in den obigen Beispielen gesehen haben.
Eine continue
ist nur innerhalb einer Schleife möglich.
Wir haben drei Arten von Schleifen behandelt:
while
– Die Bedingung wird vor jeder Iteration überprüft.
do..while
– Die Bedingung wird nach jeder Iteration überprüft.
for (;;)
– Die Bedingung wird vor jeder Iteration überprüft, zusätzliche Einstellungen verfügbar.
Um eine „Endlosschleife“ zu erstellen, wird normalerweise das while(true)
-Konstrukt verwendet. Eine solche Schleife kann, wie jede andere auch, mit der break
Direktive gestoppt werden.
Wenn wir in der aktuellen Iteration nichts tun möchten und mit der nächsten fortfahren möchten, können wir die continue
-Direktive verwenden.
Unterstützungsetiketten vor der Schleife break/continue
. Eine Bezeichnung ist die einzige Möglichkeit für break/continue
, einer verschachtelten Schleife zu entkommen und zu einer äußeren zu gelangen.
Wichtigkeit: 3
Was ist der letzte Wert, der von diesem Code gemeldet wird? Warum?
sei i = 3; während (i) { alarm( i-- ); }
Die Antwort: 1
.
sei i = 3; während (i) { alarm( i-- ); }
Jede Schleifeniteration verringert i
um 1
. Die Prüfung while(i)
stoppt die Schleife, wenn i = 0
.
Somit bilden die Schritte der Schleife die folgende Reihenfolge („Schleife abgerollt“):
sei i = 3; alarm(i--); // zeigt 3, verringert i auf 2 alarm(i--) // zeigt 2, verringert i auf 1 alarm(i--) // zeigt 1, verringert i auf 0 // fertig, while(i) check stoppt die Schleife
Wichtigkeit: 4
Notieren Sie für jede Schleifeniteration, welchen Wert sie ausgibt, und vergleichen Sie ihn dann mit der Lösung.
Beide Schleifen alert
die gleichen Werte, oder nicht?
Die Präfixform ++i
:
sei i = 0; while (++i < 5) alarm( i );
Die Postfixform i++
sei i = 0; while (i++ < 5) alarm( i );
Die Aufgabe zeigt, wie Postfix-/Präfixformen bei Vergleichen zu unterschiedlichen Ergebnissen führen können.
Von 1 bis 4
sei i = 0; while (++i < 5) alarm( i );
Der erste Wert ist i = 1
, da ++i
zuerst i
inkrementiert und dann den neuen Wert zurückgibt. Der erste Vergleich ist also 1 < 5
und die alert
zeigt 1
an.
Dann folgen 2, 3, 4…
– die Werte werden nacheinander angezeigt. Der Vergleich verwendet immer den inkrementierten Wert, da ++
vor der Variablen steht.
Schließlich wird i = 4
auf 5
erhöht, der Vergleich while(5 < 5)
schlägt fehl und die Schleife stoppt. Daher wird 5
nicht angezeigt.
Von 1 bis 5
sei i = 0; while (i++ < 5) alarm( i );
Der erste Wert ist wieder i = 1
. Die Postfixform von i++
erhöht i
und gibt dann den alten Wert zurück, sodass beim Vergleich i++ < 5
i = 0
verwendet wird (im Gegensatz zu ++i < 5
).
Der alert
ist jedoch separat. Es handelt sich um eine weitere Anweisung, die nach der Inkrementierung und dem Vergleich ausgeführt wird. Es erhält also den Strom i = 1
.
Dann folgen Sie 2, 3, 4…
Bleiben wir bei i = 4
stehen. Die Präfixform ++i
würde es erhöhen und 5
im Vergleich verwenden. Aber hier haben wir die Postfix-Form i++
. Es erhöht also i
auf 5
, gibt aber den alten Wert zurück. Daher ist der Vergleich tatsächlich while(4 < 5)
– wahr, und die Steuerung geht zu alert
über.
Der Wert i = 5
ist der letzte, da im nächsten Schritt while(5 < 5)
falsch ist.
Wichtigkeit: 4
Notieren Sie für jede Schleife, welche Werte sie anzeigen soll. Vergleichen Sie dann mit der Antwort.
Beide Schleifen alert
die gleichen Werte oder nicht?
Das Postfix-Formular:
for (let i = 0; i < 5; i++) alarm( i );
Die Präfixform:
for (let i = 0; i < 5; ++i) alarm( i );
Die Antwort: in beiden Fällen von 0
bis 4
.
for (let i = 0; i < 5; ++i) alarm( i ); for (let i = 0; i < 5; i++) alarm( i );
Das lässt sich leicht aus dem Algorithmus von for
ableiten:
Einmal i = 0
vor allem ausführen (beginnen).
Überprüfen Sie die Bedingung i < 5
Wenn true
– führen Sie den Schleifenkörper alert(i)
und dann i++
aus
Das Inkrement i++
ist von der Bedingungsprüfung (2) getrennt. Das ist nur eine weitere Aussage.
Der vom Inkrement zurückgegebene Wert wird hier nicht verwendet, daher gibt es keinen Unterschied zwischen i++
und ++i
.
Wichtigkeit: 5
Verwenden Sie die for
Schleife, um gerade Zahlen von 2
bis 10
auszugeben.
Führen Sie die Demo aus
for (sei i = 2; i <= 10; i++) { if (i % 2 == 0) { alarm( i ); } }
Wir verwenden den „Modulo“-Operator %
um den Rest zu ermitteln und prüfen hier die Gleichmäßigkeit.
Wichtigkeit: 5
Schreiben Sie den Code neu und ändern Sie die for
Schleife in while
ohne ihr Verhalten zu ändern (die Ausgabe sollte gleich bleiben).
for (sei i = 0; i < 3; i++) { alarm( `Nummer ${i}!` ); }
sei i = 0; while (i < 3) { alarm( `Nummer ${i}!` ); i++; }
Wichtigkeit: 5
Schreiben Sie eine Schleife, die zur Eingabe einer Zahl größer als 100
auffordert. Wenn der Besucher eine andere Nummer eingibt, bitten Sie ihn, die Nummer erneut einzugeben.
Die Schleife muss nach einer Zahl fragen, bis der Besucher entweder eine Zahl größer als 100
eingibt oder die Eingabe abbricht bzw. eine leere Zeile eingibt.
Hier können wir davon ausgehen, dass der Besucher nur Zahlen eingibt. In dieser Aufgabe ist es nicht erforderlich, eine spezielle Behandlung für eine nicht numerische Eingabe zu implementieren.
Führen Sie die Demo aus
lass num; Tun { num = prompt("Geben Sie eine Zahl größer als 100 ein?", 0); } while (num <= 100 && num);
Die Schleife do..while
wird wiederholt, solange beide Prüfungen wahr sind:
Die Prüfung auf num <= 100
, d. h. der eingegebene Wert ist immer noch nicht größer als 100
.
Die Prüfung && num
ist falsch, wenn num
null
oder eine leere Zeichenfolge ist. Dann stoppt auch die while
Schleife.
PS: Wenn num
null
ist, ist num <= 100
true
. Ohne die zweite Prüfung würde die Schleife also nicht anhalten, wenn der Benutzer auf CANCEL klickt. Beide Prüfungen sind erforderlich.
Wichtigkeit: 3
Eine ganze Zahl größer als 1
wird Primzahl genannt, wenn sie nur durch 1
und sich selbst ohne Rest geteilt werden kann.
Mit anderen Worten: n > 1
ist eine Primzahl, wenn sie nur durch 1
und n
gleichmäßig geteilt werden kann.
Beispielsweise ist 5
eine Primzahl, da sie nicht ohne Rest durch 2
, 3
und 4
geteilt werden kann.
Schreiben Sie den Code, der Primzahlen im Intervall von 2
bis n
ausgibt.
Für n = 10
ist das Ergebnis 2,3,5,7
.
PS: Der Code sollte für alle n
funktionieren und nicht auf einen festen Wert abgestimmt sein.
Für diese Aufgabe gibt es viele Algorithmen.
Lassen Sie uns eine verschachtelte Schleife verwenden:
Für jedes i im Intervall { Überprüfen Sie, ob ich einen Teiler von 1..i habe wenn ja => der Wert ist keine Primzahl Wenn nein => der Wert ist eine Primzahl, zeige ihn }
Der Code mit einem Etikett:
sei n = 10; nextPrime: for (let i = 2; i <= n; i++) { // für jedes i... for (let j = 2; j < i; j++) { // Suche nach einem Teiler.. if (i % j == 0) continue nextPrime; // keine Primzahl, gehe als nächstes i } alarm( i ); // eine Primzahl }
Es gibt viel Raum zur Optimierung. Beispielsweise könnten wir nach den Teilern von 2
bis zur Quadratwurzel von i
suchen. Aber wenn wir bei großen Intervallen wirklich effizient sein wollen, müssen wir den Ansatz ändern und uns auf fortgeschrittene Mathematik und komplexe Algorithmen wie quadratisches Sieb, allgemeines Zahlenfeldsieb usw. verlassen.