Grundlagen von Schleifen in TypeScript: while, do-while, for, break und continue
Erfahren Sie, wie die while-, do-while- und for-Schleifen in TypeScript ihre Bedingungen auswerten, warum sich der Gültigkeitsbereich von Schleifenvariablen die Ergebnisse verändert und wie break und continue den Ablauf beeinflussen.
Quellen: Mastering Loops in TypeScript: while, do-while, for, break & continue
Schleifen ermöglichen es Ihnen, denselben Codeblock so lange wiederholt auszuführen, wie eine Bedingung erfüllt ist, anstatt einen Befehl immer wieder zu kopieren. Sie erscheinen einfach, doch die meisten Fehler bei Schleifen entstehen durch drei Aspekte: wann die Bedingung überprüft wird, wo sich die Schleifenvariable befindet und wo sich break oder continue im Schleifenkörper befinden. Diese Anleitung geht auf TypeScript’s while, do-while und for-Schleifen ein, wobei kleine Beispiele verwendet werden, deren Ausgabe Sie Zeile für Zeile vorhersagen können, damit Sie Iterationscode lesen und schreiben können, ohne überrascht zu werden.
Warum Iteration besser ist als Wiederholung
Um die Zahlen von 1 bis 10 auszugeben, ohne eine Schleife zu verwenden, sind zehn Aufrufe von console.log notwendig. Mit einer Schleife beschreiben Sie die Aufgabe einmal, und der Laufzeitmechanismus wiederholt sie – dabei bleibt die Größe des Codes unverändert, egal ob zehn oder zehntausend Wiederholungen erforderlich sind.
Die while-Schleife: Zuerst Überprüfung, dann Ausführung
Eine while-Schleife hat die einfachste Struktur von allen:
while (condition) {
// statements
}
Die Bedingung wird vor jedem Durchlauf bewertet. Solange sie wahr ist, wird der Schleifenkörper ausgeführt; sobald sie falsch ist, endet die Schleife. Eine funktionierende while-Schleife benötigt drei Bestandteile:
- einen Anfangswert für die Variable, von der die Bedingung abhängt;
- die Bedingung selbst;
- einen Bestandteil im Schleifenkörper, der diese Variable so verändert, dass die Bedingung schließlich
falschwird.
Fehlt der dritte Bestandteil, hört die Schleife niemals auf – ein Problem, das weiter unten behandelt wird.
Zählen von 1 bis 10
Hier sind alle drei Bestandteile sichtbar: i beginnt bei 1, die Schleife läuft solange i <= 10 gilt, und i++ erhöht den Wert bei jedem Durchlauf:
let i: number = 1; // initialization
while (i <= 10) {
console.log(i);
i++;
}
Filtern auf gerade Zahlen
Um nur die geraden Zahlen in diesem Bereich auszudrucken, besuchen Sie weiterhin jeden Wert, fügen aber eine Überprüfung innerhalb des Codes hinzu. Der Modulo-Operator % gibt den Rest einer Division zurück, sodass i % 2 == 0 genau dann wahr ist, wenn i gerade ist:
let i: number = 1;
while (i <= 10) {
if (i % 2 == 0) {
console.log(i);
}
i++;
}
Es gibt einen effizienteren Weg zu einem ähnlichen Ergebnis: Überspringen Sie die Überprüfung ganz und erhöhen Sie den Wert jeweils um zwei. Beachten Sie, dass diese Version bei 0 beginnt, wodurch auch 0 in der Ausgabe enthalten ist:
let i: number = 0;
while (i <= 10) {
console.log(i);
i = i + 2;
}
Ausgabe: 0, 2, 4, 6, 8, 10
Filtern von ungeraden Zahlen
Durch Umkehrung der Vergleichsbedingung auf != 0 werden stattdessen die ungeraden Werte ausgewählt. Bei einem Startwert von 0 ist das hier unproblematisch, da 0 gerade ist und somit gefiltert wird:
let i: number = 0;
while (i <= 10) {
if (i % 2 != 0) {
console.log(i);
}
i++;
}
Ausgabe: 1, 3, 5, 7, 9
In diesen Beispielen werden == und != verwendet; die strengeren Formen === und !== werden in der Regel bevorzugt, da sie niemals Typumwandlungen vornehmen, obwohl bei zwei Zahlen das Ergebnis identisch ist.
Rückzählen
Um rückwärts zu zählen, beginnt man mit einem hohen Wert und verringert ihn solange, wie der Wert mindestens 1 beträgt:
let i: number = 10;
while (i >= 1) {
console.log(i);
i--;
}
Unendliche Schleifen und ihre Entstehung
while-Bedingung ist lediglich eine Ausdruck, der einen booleschen Wert ergibt, weshalbwhile (true) völlig gültig ist. Die untenstehende Schleife gibt ewig Ausgabe, weil nichts sie beendet:let i: number = 1;
while (true) {
console.log(i);
i++;
// no break, no exit condition this runs forever
}
Oft entstehen endlose Schleifen versehentlich: Die Steuerungsvariable wird nie aktualisiert oder bewegt sich in die falsche Richtung. Eine außer Kontrolle geratene Schleife blockiert alles andere auf ihrem Thread, wodurch eine Browser-Tab oder ein Node.js-Prozess scheinbar hängen bleibt. Ein absichtlich verwendetes while (true) ist in Ordnung, wenn es mit einem expliziten Ausstieg kombiniert wird – meist break (siehe unten) – beispielsweise in einer „bis zum Erfolg wiederholen“-Logik.
Die do-while-Schleife: einmal ausführen, dann prüfen
In einer do-while-Schleife wird die Bedingung an das Ende verschoben:
do {
// statements
} while (condition);
Weil die Prüfung nach dem Schleifenkörper stattfindet, wird dieser immer mindestens einmal ausgeführt, unabhängig von der Bedingung. Im Gegensatz dazu kann ein einfacher while-Schleifenkörper gar nicht ausgeführt werden.
Wenn die Bedingung anfangs wahr ist, verhält sich eine do-while-Schleife genauso wie eine while-Schleife:
let i: number = 1;
do {
console.log(i);
i++;
} while (i <= 5);
Der Unterschied zeigt sich, wenn die Bedingung von Anfang an falsch ist:
let i: number = 10;
do {
console.log(i);
i++;
} while (i <= 5);
Ausgabe: 10
Der Codeblock wird bedingungslos ausgeführt, wodurch 10 ausgegeben und i auf 11 erhöht wird. Erst danach wird i <= 5 bewertet; da dieses false ist, endet die Schleife nach einer Iteration. Eine while (i <= 5)-Schleife, die bei 10 beginnt, würde nichts ausgeben. Verwenden Sie do-while, wenn eine Ausführung unbedingt erforderlich ist, beispielsweise um mindestens einmal eine Anfrage anzuzeigen.
Die for-Schleife: Alles in einem Header
Die for-Schleife ist die, die Sie am häufigsten verwenden werden, insbesondere dann, wenn Sie im Voraus wissen, wie oft sie wiederholt werden soll oder um wie viel der Zähler ändern muss. Sie kombiniert Initialisierung, Bedingung und Aktualisierung in einem einzigen Header, wodurch sie kompakter ist als die entsprechende while-Schleife:
for (initialization; condition; increment/decrement) {
// statements
}
Die Reihenfolge, in der diese Teile ausgeführt werden, ist zu merken:
- Die Initialisierung erfolgt einmal, bevor alles andere (zum Beispiel
let i = 1). - Die Bedingung wird geprüft. Wenn sie
trueist, wird der Loop-Körper ausgeführt; wenn siefalseist, endet der Loop. - Nach dem Körper wird die Aktualisierungsexpression ausgeführt (zum Beispiel
i++). - Der Kontrollfluss kehrt zum Schritt 2 zurück, und der Zyklus wiederholt sich, bis die Bedingung
falseist.
Anders ausgedrückt: Die Initialisierung findet nur einmal statt, während die Bedingung und die Aktualisierung in jedem Zyklus erfolgen.
Zählen von 1 bis 10
for (let i = 1; i <= 10; i++) {
console.log(i);
}
Zwei Möglichkeiten, gerade Zahlen zu erhalten
Ebenso wie bei while kann man auch in Schritten von zwei vorgehen:
for (let i = 2; i <= 10; i += 2) {
console.log(i);
}
oder jeden Wert durchlaufen und innerhalb des Körpers filtern:
for (let i = 2; i <= 10; i++) {
if (i % 2 == 0) {
console.log(i);
}
}
Beide geben dieselbe Sequenz aus:
2
4
6
8
10
Das Schrittweises Vorgehen erledigt weniger Arbeit, da es nur gerade Werte berücksichtigt; das Filtern ist flexibler, wenn die Regel kein fester Schritt ist, wie zum Beispiel „durch 3 oder 7 teilbar“.
Rückzählen
Durch eine abnehmende Aktualisierung wird die Schleife rückwärts ausgeführt:
for (let i = 10; i >= 1; i--) {
console.log(i);
}
Ausgabe: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
Warum ist die Schleifenvariable nach der Schleife noch 6?
Der Ort, an dem die Schleifenvariable deklariert wird, bestimmt, was nach der Schleife vorhanden ist. In diesem Beispiel wird i vor der Schleife deklariert, und der for-Header weist ihr nur einen Wert zu:
let i: number;
for (i = 1; i <= 5; i++) {
console.log(i);
}
console.log(i);
Ausgabe: 1, 2, 3, 4, 5, 6
Weil i im äußeren Scope liegt, kann die letzte Anweisung console.log(i) es weiterhin abrufen und den Wert speichern, den der Loop hinterlassen hat. Das ist 6 und nicht 5 – aufgrund der oben beschriebenen Reihenfolge der Operationen: Bei der letzten Durchlaufschleife beträgt i 5, der Loop-Body gibt diesen Wert aus, die Aktualisierung erhöht ihn auf 6, und erst danach scheitert 6 <= 5 und beendet den Loop.
Der häufigere Stil, for (let i = 1; i <= 5; i++), weist i einen Blockscope zu, der auf den Loop beschränkt ist; danach existiert es nicht mehr. Der Versuch, darauf nach dem Loop zuzugreifen, führt in TypeScript zu einem Kompilierfehler („Cannot find name 'i'“), und in reinem JavaScript würde dies zur Laufzeit einen ReferenceError auslösen. Es ist in der Regel besser, die Variable im Header zu deklarieren; eine äußere Deklaration sollte nur dann verwendet werden, wenn der endgültige Wert benötigt wird.
Anderung des Ablaufs mit break und continue
Zwei Sprunganweisungen ermöglichen es Ihnen, den normalen Ablauf einer Schleife von innen heraus zu verändern:
breakbeendet die Schleife umgehend, unabhängig davon, wie viele Iterationen noch ausstehen.continueverlässt nur die aktuelle Iteration und geht zur nächsten über; die Schleife läuft weiter.
In einer for-Schleife wird mit continue dennoch die Aktualisierungsexpression ausgeführt, bevor die Bedingung erneut überprüft wird, weshalb der Zähler weiter zunimmt.
break: Die Position innerhalb des Schleifenkörpers ist wichtig
Hier erfolgt die Überprüfung mit break vor dem Ausgeben:
let i: number;
for (i = 1; i <= 10; i++) {
if (i == 5) {
break;
}
console.log(i);
}
Ausgabe: 1, 2, 3, 4
Wenn i den Wert 5 erreicht, verlässt die Schleife bereits vor dem Ausführen von console.log, wodurch 5 nie angezeigt wird.
Führt man das Ausgeben oberhalb der Überprüfung aus, ändert sich das Ergebnis:
let i: number;
for (i = 1; i <= 10; i++) {
console.log(i);
if (i == 5) {
break;
}
}
Ausgabe: 1, 2, 3, 4, 5
Die Bedingung bleibt unverändert, doch nun wird 5 angezeigt, weil die Ausgabe vor der Überprüfung erfolgt. Sowohl bei break als auch bei continue bestimmt die Reihenfolge der Anweisungen im Schleifenkörper die Ausgabe.
continue: Einzelnen Wert überspringen
Durch Ersetzung von break durch continue wird „bei 5 stoppen“ in „5 überspringen“ umgewandelt:
let i: number;
for (i = 1; i <= 10; i++) {
if (i == 5) {
continue;
}
console.log(i);
}
Ausgabe: 1, 2, 3, 4, 6, 7, 8, 9, 10
Die Schleife läuft weiter; lediglich die Ausgabe für diese eine Iteration wird ausgelassen.
Jeden ungeraden Wert überspringen
In Kombination mit einem Filter leitet continue jeden ungeraden Wert direkt zur nächsten Iteration weiter:
let i: number;
for (i = 0; i <= 10; i++) {
if (i % 2 != 0) {
continue;
}
console.log(i);
}
Ausgabe: 0, 2, 4, 6, 8, 10
Eine Liste spezifischer Werte überspringen
Die Bedingung kann so komplex sein, wie Sie es benötigen. Mit dem logischen OR-Operator || überspringt diese Schleife die Werte 3, 5 und 7:
let i: number;
for (i = 0; i <= 10; i++) {
if (i == 3 || i == 5 || i == 7) {
continue;
}
console.log(i);
}
Ausgabe: 0, 1, 2, 4, 6, 8, 9, 10
Für eine längere Liste zur Überspringung liest sich [3, 5, 7].includes(i) oder ein Set besser.
Wichtige Erkenntnisse
whileprüft seine Bedingung vor jedem Durchlauf und kann null Mal ausgeführt werden;do-whileprüft sie danach und wird immer mindestens einmal ausgeführt.- Jede Schleife benötigt eine Aktualisierung, die letztendlich die Bedingung falsch macht; andernfalls entsteht eine endlose Schleife, die den Thread blockiert.
- In einer
for-Schleife wird die Initialisierung einmal ausgeführt, während die Bedingung und die Aktualisierung in jedem Zyklus erfolgen – deshalb endet ein Zähler einen Schritt über dem Limit.
let im Anfangsteil der Schleife bleibt er auf diese beschränkt; eine Deklaration außerhalb sorgt dafür, dass sein endgültiger Wert erhalten bleibt.break beendet die Schleife und continue überspringt eine Iteration; der Platz, an dem sie im Verhältnis zu anderen Anweisungen platziert werden, beeinflusst das Ergebnis.Bedingungen entscheiden, welcher Code ausgeführt wird; Schleifen bestimmen, wie oft. Zusammen decken sie den größten Teil des Kontrollflusses ab, den man schreiben wird.