JavaScript 流程控制 (Control flow)
JavaScript 流程控制 (Control flow)
一個程式未必一定要由上到下依序執行,我們可以設定一些條件來控制一個程式遇到某個狀況時,該怎麼執行不同的程式邏輯,
亦即是控制程式的執行流程。 JavaScript 提供了以下這些 流程控制 的語法:
if...else
switch
for
while
label
try catch finally
if...else 語法
if
if(condition){
// statements
}
當 condition 為 true 的時候,才執行大括號裡面的語句
如果是 false 則跳過整個 if 區塊。 例如:
if (age > 18) {
status = 'adult';
adultNum += 1; }
if else
if(condition){
statement 1 ;
} else {
statement 2 ;
}
當 conditiOn 為 true 的時候,才執行大括號裡面的語句
如果是 false 則執行 else 區塊裡面的語句。 例如:
if (age > 18) {
status = 'adult';
adultNum += 1;
} else {
status = 'minor';
minorNum += 1;
}
else if
if(condition_1 ){
statement 1 ;
} else if(condition_2){
statement 2 ;
} else if(condition_n){
statement n ;
} else{
statement last ;
}
可以用很多個 else if 切分更多個條件區塊,程式會從上到下執行每一個 condition,
直到第一個 condition 為 true 的區塊,且就只會執行這個區塊的語句;
但如果 condition 全部都是 false 則執行 else 區塊
(你也可以省略 最後的 else 區塊,則會全部跳過什麼都不執行)。
Falsy values
還記得嗎?在運算子有提到過的,JavaScript 只有對下面這些值會判斷為 false 其他都是 true:
布林值 false undefined null 數值 0 NaN 空字串 ''
例如:
var text = ""; // text 是 空字串
if (text){
// 我不會被執行
} else {
// 我會被執行
}
switch 語法
JavaScript 遇到 switch 語句會先執行指定的 expression 語句,然後用執行 expression 得到的值,
去跟所有 case 的值做比較,如果相等就執行這個 case 區塊的程式碼,都不相等則執行 default 區塊的程式碼。
switch (expression) {
case value_1:
statements_1;
break;
case value_2:
statements_2;
break;
...
default:
statements_def
break;
}
例如:
switch (fruitType) {
case 'Orange':
alert('Orange');
break;
case 'Apple':
alert('Apple');
break;
case 'Banana':
alert('Banana');
break;
default:
alert('無符合的條件');
}
此例中,如果 fruitType 變數的值是 Apple,則會執行 case 'Apple' 的區塊;
如果 fruitType 變數的值是 Banana,則會執行 case 'Banana' 區塊的程式碼;
如果 fruitType 的值都不在指定的 case 裡面,則執行 default 區塊的程式碼。
你也可以省略 default 區塊,若找不到相符的 case,
則跳過整個 switch 區塊什麼不執行。
break 關鍵字
在上面你有看到我們用了 break,那 break 的用途是什麼呢?當 JavaScript 執行到 break 這個關鍵字的時後,就會直接跳出整個 switch 區塊,繼續往下執行。
而如果沒有 break 則程式會從符合的 case 區塊開始,一路往下執行到遇到 break 為止!
例如上面的例子,我們拿掉 Apple 區塊裡面的 break,則如果 fruitType 是 Apple 的時候,會同時執行 Apple, 和 Banana 區塊,直到碰到 Banana 區塊中的 break 為止才會跳出整個 switch 區塊
這種行為我們稱做 Fall-Through。我們可以利用這個特性,撰寫像是這類程式碼:
switch (new Date().getDay()) {
case 1:
case 2:
case 3:
case 4:
case 5:
alert('工作天 :(');
break;
case 6:
case 0:
alert('週末 :)');
}
for 迴圈語法
for 迴圈用來重覆執行 for 區塊內的語句。
語法:
for ( initialExpression; condition; incrementExpression ) {
// statements
}
其中 initialExpression 是初始化語句,在迴圈第一次執行之前,會先執行 initialExpression。在每一次執行迴圈之前,都會先執行 condition 條件式,
如果值是 true 則執行迴圈,false 則跳出 for 迴圈。而在每一次執行完迴圈之後,都會執行 incrementExpression 語句。
例如:
var counter = 0;
for (var i = 0; i < 5 ; i++) {
counter += i;
}
在迴圈開始之前,會先初始化 變數 i = 0 ,
當每一次迴圈開始執行之前,會判斷 i 是否小於 5,如果大於 5 則跳出迴圈,小於 5 則執行迴圈內容,
而每當執行完一次迴圈後,會將 變數 i 加 1。
所以每一個迴圈,變數 i 分別會是 0, 1, 2, 3, 4, 5,直到 i 的值變成 5 時則跳出迴圈 ,
所以上面的迴圈執行完後,最後 counter 的值會是 10 ! ( 註 : counter += i 即 counter = counte + i )
for ( var i = 0 , j=10 ; i < 5 ; i++ ) { → 另外,在 initialExpression 語句中,你還可以用逗點 , 隔開多個初始化語句。
// .....
}
var i = 0;
var counter = 0;
for ( ; i < 5; ) { → 也可以省略 initialExpression 或 incrementExpression 語句。
counter += i;
i += 1;
}
break break 用來跳出整個 for 迴圈,而讓程式 往下執行。
continue continue 用來讓程式跳過 此迴圈 剩餘的程式碼,繼續直接執行 下一次迴圈。
var counter = 0;
for (var i = 0; i < 5; i++) {
if (i < 3) {
continue;
}
counter += i;
}
則當 for 迴圈執行完後 counter 的值會是 7,因為當 i 等於 0, 1, 2 的時候都被跳過 continue 之後的程式碼,
直到 i 等於 3, 4 的時候才會執行迴圈內容。 ( 註: counter += i 即 counter = counte + i )
while 迴圈語法
while 語法
跟 for 語法用途一樣,while 是另一種迴圈語法,用來重覆執行 while 區塊內的語句。
語法:
while (condition){
// statements
}
當 condition 為 true 時,則重覆執行 while 區塊內的語句, 直至 condition 變 false。
例如:
var n = 0;
var x = 0;
while (n < 3) {
n++;
x += n;
}
每一次迴圈開始執行之前,都會先判斷 n 是否小於 3,如果不是則跳出迴圈。
在每一次回圈內都會將 n 加 1,將 x 加上 n,
所以當 while 迴圈執行結束後,x 的值會是 6。
跟 for 區塊一樣,while 區塊內一樣可以使用 break 和 continue 關鍵字,來讓你可跳出 while 或直接執行下一次迴圈。
do...while 語法
while 還有另一種語法,用來讓你可以確保迴圈
至少 被執行一次 (第一次)。
語法:
do {
// statements
} while (condition);
例如: var i = 10;
do {
i += 1;
} while (i < 5);
此例中,i += 1 無論如何都會被執行一次,在第一次迴圈中 i 會被加 1 等於 11,
而條件式 i < 5 會是 false,
所以會跳出 while 迴圈,執行完 do...while 後 i 變數的值會變成 11。
try catch finally Error Handling (例外處理)
例外處理 (error handling) 是 JavaScript 的一種程式流程控制,你可以在程式執行可能拋出錯誤的地方使用,主動捕捉並處理錯誤,避免整個程式因為發生錯誤而停止執行。
語法:
try {
// 預期可能會發生錯誤的程式碼
} catch (e) {
// try 區塊有拋出錯誤時,則執行這裡的程式碼
} finally {
// finally 區塊的程式碼一定會在最後被執行 . 你可以省略 finally 區塊
}
把可能出錯的程式碼放在 try 區塊中;
然後出錯時的處理程式碼放在 catch 區塊中;
而放在 finally 區塊中的程式碼
無論如何都會在最後被執行。
JavaScript 會有這些類型的錯誤:
錯誤類型 | 說明 |
EvalError | eval() 執行錯誤 |
RangeError | 一個數值超過允許範圍 |
ReferenceError | 存取未宣告的變數 |
SyntaxError | 程式語法錯誤 |
TypeError | 型別錯誤 |
URIError | URI handling 相關函數例如 decodeURIComponent() 執行時錯誤 |
try-catch-finally 用法例子:
try {
blah('Hello world');
} catch(err) {
alert(err.name + ': ' + err.message);
} finally {
alert('try catch 區塊結束');
}
因為我們沒有宣告 blah 這函數,呼叫 blah 函數時會發生錯誤,
因為程式發生錯誤的位置在 try 區塊,所以錯誤會被 catch 區塊捕捉到,
上面程式碼依序會跳出視窗 "ReferenceError: blah is not defined"
-> "try catch 區塊結束"。
catch 區塊會接受一個參數,代表錯誤物件 (Error Object),錯誤物件有兩個屬性:
name 表示錯誤類型,例如 "ReferenceError"
message 說明為什麼錯誤的文字訊息
主動拋出例外錯誤 throw
你可以用 throw 關鍵字在程式中主動拋出錯誤,拋出的例外可以是一個字串 (string)、數字 (number) 或錯誤物件 (error object)。
語法:
throw err;
例如:
try {
throw 'myException';
} catch (err) {
// err 是字串 "myException"
err ;
}
try {
throw 101;
} catch (err) {
// err 是數字 101
err ;
}
JavaScript 有一個 Error 物件,所有的例外錯誤都是繼承自 Error 物件。你可以客製化一個自己的錯誤物件 - new Error():
try {
throw new Error('oops');
} catch (err) {
// 輸出 "Error: oops"
console.log(err.name + ': ' + err.message);
}
instanceof
如果預期拋出的錯誤可能會有很多種類型,我們可以用 instanceof 來判斷該錯誤是哪一類錯誤:
try {
foo.bar();
} catch (err) {
if (err instanceof EvalError) { // 如果錯誤類型是 EvalError
console.log(err.name + ': ' + err.message);
} else if (err instanceof RangeError) { // 如果錯誤類型是 RangeError
console.log(err.name + ': ' + err.message);
}
// ...
}
label (標籤)
label 可以用來標記一個迴圈,label 用來搭配 break 和 continue 一起使用。
語法:
label:
statement
break [label];
continue [label];
用法例如:
var x = 0;
var z = 0;
labelCancelLoops: // 把外層的迴圈標記叫做 labelCancelLoops
while (true) {
console.log('Outer loops: ' + x);
x += 1;
z = 1;
while (true) {
console.log('Inner loops: ' + z);
z += 1;
if (z === 3 && x === 3) { // 跳出 labelCancelLoops 迴圈
break labelCancelLoops;
} else if (z === 3) { // 跳出當前迴圈
break;
}
}
}
此例程式碼會輸出:
Outer loops: 0
Inner loops: 1
Inner loops: 2
Outer loops: 1
Inner loops: 1
Inner loops: 2
Outer loops: 2
Inner loops: 1
Inner loops: 2
可以發現,當你有多層迴圈的時候,label 可以用來很方便的直接跳出外層的迴圈!
用 break; 只會跳出當前這「一個」迴圈。
搭配 continue 也是類似的用法:
var i, j;
loop1: // 把外層的迴圈標記叫做 loop1
for (i = 0; i < 3; i++) {
loop2: // 把內層的迴圈標做 loop2
for (j = 0; j < 3; j++) {
if (i === 1 && j === 1) { // 跳出 loop1 迴圈
continue loop1;
}
console.log('i = ' + i + ', j = ' + j);
}
}
此例程式碼會輸出:
i = 0, j = 0
i = 0, j = 1
i = 0, j = 2
i = 1, j = 0
i = 2, j = 0
i = 2, j = 1
i = 2, j = 2