我們常常需要重複動作。
例如,依序從清單中輸出商品,或只是針對 1 到 10 的每個數字執行相同的程式碼。
迴圈是一種重複執行相同程式碼多次的方式。
「while」迴圈
while
迴圈具有以下語法
while (condition) {
// code
// so-called "loop body"
}
在 condition
為真時,將執行迴圈主體中的 code
。
例如,以下迴圈會在 i < 3
時輸出 i
let i = 0;
while (i < 3) { // shows 0, then 1, then 2
alert( i );
i++;
}
迴圈主體的單次執行稱為一次迭代。上述範例中的迴圈會進行三次迭代。
如果上述範例中沒有 i++
,理論上迴圈將會永遠重複下去。實際上,瀏覽器會提供方法來停止此類迴圈,而在伺服器端 JavaScript 中,我們可以終止程序。
任何表達式或變數都可以成為迴圈條件,而不仅仅是比較:while
會評估條件並將其轉換為布林值。
例如,撰寫 while (i != 0)
的較短方式為 while (i)
let i = 3;
while (i) { // when i becomes 0, the condition becomes falsy, and the loop stops
alert( i );
i--;
}
如果迴圈主體只有一個陳述式,我們可以省略大括號 {…}
let i = 3;
while (i) alert(i--);
「do…while」迴圈
條件檢查可以使用 do..while
語法移到迴圈主體下方
do {
// loop body
} while (condition);
迴圈會先執行主體,然後檢查條件,並且在為真的情況下,會重複執行。
例如
let i = 0;
do {
alert( i );
i++;
} while (i < 3);
此種語法形式僅應在您希望迴圈主體在條件為真時執行至少一次時使用。通常,較佳的選擇是另一種形式:while(…) {…}
。
「for」迴圈
for
迴圈較為複雜,但也是最常用的迴圈。
它看起來像這樣
for (begin; condition; step) {
// ... loop body ...
}
讓我們透過範例瞭解這些部分的含義。以下迴圈會執行 alert(i)
,其中 i
從 0
到 3
(但不包括 3
)
for (let i = 0; i < 3; i++) { // shows 0, then 1, then 2
alert(i);
}
讓我們逐一檢視 for
陳述式
部分 | ||
---|---|---|
開頭 | let i = 0 |
在進入迴圈時執行一次。 |
條件 | i < 3 |
在每次迴圈迭代之前檢查。如果為假,迴圈就會停止。 |
主體 | alert(i) |
在條件為真的情況下重複執行。 |
步驟 | i++ |
在每次迭代中,於主體後執行。 |
一般迴圈演算法運作方式如下
Run begin
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ ...
也就是說,begin
執行一次,然後進行迭代:在每次 condition
測試後,執行 body
和 step
。
如果您不熟悉迴圈,可以回到範例並在紙上逐步重現其執行方式,這可能會有幫助。
以下是我們案例中確切發生的情況
// for (let i = 0; i < 3; i++) alert(i)
// run begin
let i = 0
// if condition → run body and run step
if (i < 3) { alert(i); i++ }
// if condition → run body and run step
if (i < 3) { alert(i); i++ }
// if condition → run body and run step
if (i < 3) { alert(i); i++ }
// ...finish, because now i == 3
在此,「計數器」變數 i
直接在迴圈中宣告。這稱為「內聯」變數宣告。此類變數僅在迴圈內可見。
for (let i = 0; i < 3; i++) {
alert(i); // 0, 1, 2
}
alert(i); // error, no such variable
我們可以使用現有變數,而非定義變數
let i = 0;
for (i = 0; i < 3; i++) { // use an existing variable
alert(i); // 0, 1, 2
}
alert(i); // 3, visible, because declared outside of the loop
略過部分
for
的任何部分都可以略過。
例如,如果我們不需要在迴圈開始時執行任何動作,我們可以省略 begin
。
如下所示
let i = 0; // we have i already declared and assigned
for (; i < 3; i++) { // no need for "begin"
alert( i ); // 0, 1, 2
}
我們也可以移除 step
部分
let i = 0;
for (; i < 3;) {
alert( i++ );
}
這會讓迴圈與 while (i < 3)
相同。
我們實際上可以移除所有內容,建立一個無限迴圈
for (;;) {
// repeats without limits
}
請注意,必須存在兩個 for
分號 ;
。否則,會發生語法錯誤。
中斷迴圈
通常,當迴圈條件變為 false 時,迴圈會結束。
但是,我們可以使用特殊的 break
指令隨時強制結束。
例如,以下迴圈會要求使用者輸入一系列數字,當沒有輸入數字時「中斷」
let sum = 0;
while (true) {
let value = +prompt("Enter a number", '');
if (!value) break; // (*)
sum += value;
}
alert( 'Sum: ' + sum );
如果使用者輸入空白列或取消輸入,break
指令會在行 (*)
中啟動。它會立即停止迴圈,將控制權傳遞給迴圈後的第 1 行。也就是 alert
。
「無限迴圈 + 視需要使用 break
」的組合非常適合在迴圈條件必須在迴圈的開頭或結尾,而是在主體的中間甚至多個位置檢查的情況。
繼續下一個迭代
continue
指令是 break
的「精簡版」。它不會停止整個迴圈。相反地,它會停止目前的迭代並強制迴圈開始新的迭代(如果條件允許)。
如果我們已完成目前的迭代並希望繼續下一個迭代,我們可以使用它。
以下迴圈使用 continue
僅輸出奇數值
for (let i = 0; i < 10; i++) {
// if true, skip the remaining part of the body
if (i % 2 == 0) continue;
alert(i); // 1, then 3, 5, 7, 9
}
對於 i
的偶數值,continue
指令會停止執行主體,並將控制權傳遞給 for
的下一個迭代(下一個數字)。因此,alert
僅會在奇數值時呼叫。
continue
指令有助於減少巢狀顯示奇數值的迴圈看起來像這樣
for (let i = 0; i < 10; i++) {
if (i % 2) {
alert( i );
}
}
從技術角度來看,這與上述範例相同。當然,我們可以使用 if
區塊來包裝程式碼,而不是使用 continue
。
但作為副作用,這會建立另一個巢狀層級(大括號內的 alert
呼叫)。如果 if
內的程式碼長於數行,則可能會降低整體可讀性。
break/continue
在「?」的右側請注意,無法將非表達式的語法結構與三元運算子 ?
搭配使用。特別是,例如 break/continue
等指令不允許在那裡使用。
例如,如果我們採用這個程式碼
if (i > 5) {
alert(i);
} else {
continue;
}
…並使用問號重寫
(i > 5) ? alert(i) : continue; // continue isn't allowed here
…它會停止運作:出現語法錯誤。
這是另一個不使用問號運算子 ?
取代 if
的原因。
break/continue 的標籤
有時我們需要一次從多個巢狀迴圈中中斷。
例如,在以下程式碼中,我們對 i
和 j
進行迴圈,提示輸入座標 (i, j)
從 (0,0)
到 (2,2)
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Value at coords (${i},${j})`, '');
// what if we want to exit from here to Done (below)?
}
}
alert('Done!');
如果使用者取消輸入,我們需要一種方法來停止處理程序。
input
之後的普通 break
只會中斷內部迴圈。這還不夠,標籤來救援了!
標籤是迴圈前帶有冒號的識別碼
labelName: for (...) {
...
}
以下迴圈中的 break <labelName>
陳述式會中斷到標籤
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Value at coords (${i},${j})`, '');
// if an empty string or canceled, then break out of both loops
if (!input) break outer; // (*)
// do something with the value...
}
}
alert('Done!');
在上述程式碼中,break outer
向上尋找名為 outer
的標籤,並中斷該迴圈。
因此,控制權會直接從 (*)
轉到 alert('Done!')
。
我們也可以將標籤移到單獨一行
outer:
for (let i = 0; i < 3; i++) { ... }
continue
指令也可以與標籤搭配使用。在此情況下,程式碼執行會跳到標籤迴圈的下一次反覆運算。
標籤不允許我們跳到程式碼中的任意位置。
例如,不可能這樣做
break label; // jump to the label below (doesn't work)
label: for (...)
break
指令必須在程式碼區塊內。技術上來說,任何標籤程式碼區塊都可以,例如
label: {
// ...
break label; // works
// ...
}
…雖然 99.9% 的時間 break
都用於迴圈內,如我們在上述範例中看到的。
continue
只能從迴圈內執行。
摘要
我們涵蓋了 3 種類型的迴圈
while
– 每次迭代之前會檢查條件。do..while
– 每次迭代之後會檢查條件。for (;;)
– 每次迭代之前會檢查條件,可使用其他設定。
要建立一個「無限」迴圈,通常會使用 while(true)
結構。這種迴圈就像其他迴圈一樣,可以使用 break
指令停止。
如果我們不想在目前的迭代中執行任何動作,並想要轉到下一個迭代,可以使用 continue
指令。
break/continue
支援迴圈之前的標籤。標籤是 break/continue
離開巢狀迴圈並前往外層迴圈的唯一方式。
留言
<code>
標籤,若要插入多行,請將其包裝在<pre>
標籤中,若要插入 10 行以上,請使用沙盒 (plnkr、jsbin、codepen…)