咱們常常須要重複執行一些操做。javascript
例如,咱們須要將列表中的商品逐個輸出,或者運行相同的代碼將數字 1 到 10 逐個輸出。html
循環 是一種重複運行同一代碼的方法。java
while
循環的語法以下:react
while (condition) {
// 代碼
// 所謂的「循環體」
}
複製代碼
當 condition
爲 true
時,執行循環體的 code
。算法
例如,如下將循環輸出當 i < 3
時的 i
值:瀏覽器
let i = 0;
while (i < 3) { // 依次顯示 0、1 和 2
alert( i );
i++;
}
複製代碼
循環體的單次執行叫做 一次迭代。上面示例中的循環進行了三次迭代。bash
若是上述示例中沒有 i++
,那麼循環(理論上)會永遠重複執行下去。實際上,瀏覽器提供了阻止這種循環的方法,咱們能夠經過終止進程,來停掉服務器端的 JavaScript。服務器
任何表達式或變量均可以是循環條件,而不只僅是比較。在 while
中的循環條件會被計算,計算結果會被轉化爲布爾值。微信
例如,while (i != 0)
可簡寫爲 while (i)
:學習
let i = 3;
while (i) { // 當 i 變成 0 時,條件爲 false,循環終止
alert( i );
i--;
}
複製代碼
若是循環體只有一條語句,則能夠省略大括號 {…}
:
let i = 3;
while (i) alert(i--);
複製代碼
使用 do..while
語法能夠將條件檢查移至循環體 下面:
do {
// 循環體
} while (condition);
複製代碼
循環首先執行循環體,而後檢查條件,當條件爲真時,重複執行循環體。
例如:
let i = 0;
do {
alert( i );
i++;
} while (i < 3);
複製代碼
這種形式的語法不多使用,除非你但願無論條件是否爲真,循環體 至少執行一次。一般咱們更傾向於使用另外一個形式:while(…) {…}
。
for
循環更加複雜,但它是最常使用的循環形式。
for
循環看起來就像這樣:
for (begin; condition; step) {
// ……循環體……
}
複製代碼
咱們經過示例來了解一下這三個部分的含義。下述循環從 i
等於 0
到 3
(但不包括 3
)運行 alert(i)
:
for (let i = 0; i < 3; i++) { // 結果爲 0、一、2
alert(i);
}
複製代碼
咱們逐個部分分析 for
循環:
語句段 | ||
---|---|---|
begin | i = 0 |
進入循環時執行一次。 |
condition | i < 3 |
在每次循環迭代以前檢查,若是爲 false,中止循環。 |
body(循環體) | alert(i) |
條件爲真時,重複運行。 |
step | i++ |
在每次循環體迭代後執行。 |
通常循環算法的工做原理以下:
開始運行
→ (若是 condition 成立 → 運行 body 而後運行 step)
→ (若是 condition 成立 → 運行 body 而後運行 step)
→ (若是 condition 成立 → 運行 body 而後運行 step)
→ ...
複製代碼
因此,begin
執行一次,而後進行迭代:每次檢查 condition
後,執行 body
和 step
。
若是你這是第一次接觸循環,那麼回到這個例子,在一張紙上重現它逐步運行的過程,可能會對你有所幫助。
如下是在這個示例中發生的事:
// for (let i = 0; i < 3; i++) alert(i)
// 開始
let i = 0
// 若是條件爲真,運行下一步
if (i < 3) { alert(i); i++ }
// 若是條件爲真,運行下一步
if (i < 3) { alert(i); i++ }
// 若是條件爲真,運行下一步
if (i < 3) { alert(i); i++ }
// ……結束,由於如今 i == 3
複製代碼
這裏「計數」變量 i
是在循環中聲明的。這叫作「內聯」變量聲明。這樣的變量只在循環中可見。
for (let i = 0; i < 3; i++) {
alert(i); // 0, 1, 2
}
alert(i); // 錯誤,沒有這個變量。
複製代碼
除了定義一個變量,咱們也可使用現有的變量:
let i = 0;
for (i = 0; i < 3; i++) { // 使用現有的變量
alert(i); // 0, 1, 2
}
alert(i); //3,可見,由於是在循環以外聲明的
複製代碼
for
循環的任何語句段均可以被省略。
例如,若是咱們在循環開始時不須要作任何事,咱們就能夠省略 begin
語句段。
就像這樣:
let i = 0; // 咱們已經聲明瞭 i 並對它進行了賦值
for (; i < 3; i++) { // 再也不須要 "begin" 語句段
alert( i ); // 0, 1, 2
}
複製代碼
咱們也能夠移除 step
語句段:
let i = 0;
for (; i < 3;) {
alert( i++ );
}
複製代碼
該循環與 while (i < 3)
等價。
實際上咱們能夠刪除全部內容,從而建立一個無限循環:
for (;;) {
// 無限循環
}
複製代碼
請注意 for
的兩個 ;
必須存在,不然會出現語法錯誤。
一般條件爲假時,循環會終止。
但咱們隨時均可以使用 break
指令強制退出。
例如,下面這個循環要求用戶輸入一系列數字,在輸入的內容不是數字時「終止」循環。
let sum = 0;
while (true) {
let value = +prompt("Enter a number", '');
if (!value) break; // (*)
sum += value;
}
alert( 'Sum: ' + sum );
複製代碼
若是用戶輸入空行或取消輸入,在 (*)
行的 break
指令會被激活。它馬上終止循環,將控制權傳遞給循環後的第一行,即,alert
。
根據須要,"無限循環 + break
" 的組合很是適用於沒必要在循環開始/結束時檢查條件,但須要在中間甚至是主體的多個位置進行條件檢查的狀況。
continue
指令是 break
的「輕量版」。它不會停掉整個循環。而是中止當前這一次迭代,並強制啓動新一輪循環(若是條件容許的話)。
若是咱們完成了當前的迭代,而且但願繼續執行下一次迭代,咱們就可使用它。
下面這個循環使用 continue
來只輸出奇數:
for (let i = 0; i < 10; i++) {
//若是爲真,跳過循環體的剩餘部分。
if (i % 2 == 0) continue;
alert(i); // 1,而後 3,5,7,9
}
複製代碼
對於偶數的 i
值,continue
指令會中止本次循環的繼續執行,將控制權傳遞給下一次 for
循環的迭代(使用下一個數字)。所以 alert
僅被奇數值調用。
顯示奇數的循環能夠像下面這樣:
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 不容許在這個位置
複製代碼
……代碼會中止運行,並顯示有語法錯誤。
這只是不使用 ?
而不是 if
的另外一個緣由。
有時候咱們須要從一次從多層嵌套的循環中跳出來。
例如,下述代碼中咱們的循環使用了 i
和 j
,從 (0,0)
到 (3,3)
提示座標 (i, j)
:
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Value at coords (${i},${j})`, '');
// 若是我想從這裏退出並直接執行 alert('Done!')
}
}
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 (!input) break outer; // (*)
// 用獲得的值作些事……
}
}
alert('Done!');
複製代碼
上述代碼中,break outer
向上尋找名爲 outer
的標籤並跳出當前循環。
所以,控制權直接從 (*)
轉至 alert('Done!')
。
咱們還能夠將標籤移至單獨一行:
outer:
for (let i = 0; i < 3; i++) { ... }
複製代碼
continue
指令也能夠與標籤一塊兒使用。在這種狀況下,執行跳轉到標記循環的下一次迭代。
標籤不容許咱們跳到代碼的任意位置。
例如,這樣作是不可能的:
break label; // 沒法跳轉到這個標籤
label: for (...)
複製代碼
只有在循環內部才能調用 break/continue
,而且標籤必須位於指令上方的某個位置。
咱們學習了三種循環:
while
—— 每次迭代以前都要檢查條件。do..while
—— 每次迭代後都要檢查條件。for (;;)
—— 每次迭代以前都要檢查條件,可使用其餘設置。一般使用 while(true)
來構造「無限」循環。這樣的循環和其餘循環同樣,均可以經過 break
指令來終止。
若是咱們不想在當前迭代中作任何事,而且想要轉移至下一次迭代,那麼可使用 continue
指令。
break/continue
支持循環前的標籤。標籤是 break/continue
跳出嵌套循環以轉到外部的惟一方法。
先本身作題目再看答案。
重要程度:⭐️⭐️⭐
此代碼最後一次 alert 值是多少?爲何?
let i = 3;
while (i) {
alert( i-- );
}
複製代碼
重要程度:⭐️⭐️⭐️⭐
對於每次循環,寫出你認爲會顯示的值,而後與答案進行比較。
如下兩個循環的 alert
值是否相同?
前綴形式 ++i
:
let i = 0;
while (++i < 5) alert( i );
複製代碼
後綴形式 i++
let i = 0;
while (i++ < 5) alert( i ); 複製代碼
重要程度:⭐️⭐️⭐️⭐
對於每次循環,寫下它將顯示的值。而後與答案進行比較。
兩次循環 alert
值是否相同?
後綴形式:
for (let i = 0; i < 5; i++) alert( i );
複製代碼
前綴形式:
for (let i = 0; i < 5; ++i) alert( i );
複製代碼
重要程度:⭐️⭐️⭐️⭐️⭐️
使用 for
循環輸出從 2
到 10
的偶數。
重要程度:⭐️⭐️⭐️⭐️⭐️
重寫代碼,在保證不改變其行爲的狀況下,將 for
循環更改成 while
(輸出應保持不變)。
for (let i = 0; i < 3; i++) {
alert( `number ${i}!` );
}
複製代碼
重要程度:⭐️⭐️⭐️⭐️⭐️
編寫一個提示用戶輸入大於 100
的數字的循環。若是用戶輸入其餘數值 —— 請他從新輸入。
循環一直在請求一個數字,直到用戶輸入了一個大於 100
的數字,取消輸入或輸入了一個空行爲止。
在這咱們假設用戶只會輸入數字。在本題目中,不須要對非數值輸入進行特殊處理。
重要程度:⭐️⭐️⭐
大於 1
且不能被除了 1
和它自己之外的任何數整除的整數叫作素數。
換句話說,n > 1
且不能被 1
和 n
之外的任何數整除的整數,被稱爲素數。
例如,5
是素數,由於它不能被 2
、3
和 4
整除,會產生餘數。
寫一個能夠輸出 2
到 n
之間的全部素數的代碼。
當 n = 10
,結果輸出 二、三、五、7
。
P.S. 代碼應適用於任何 n
,而不是對任何固定值進行硬性調整。
在微信公衆號「技術漫談」後臺回覆 1-2-12
獲取本題答案。
現代 JavaScript 教程:開源的現代 JavaScript 從入門到進階的優質教程。React 官方文檔推薦,與 MDN 並列的 JavaScript 學習教程。
在線免費閱讀:zh.javascript.info
掃描下方二維碼,關注微信公衆號「技術漫談」,訂閱更多精彩內容。