前言
學習如何在程式碼中建立和控制資料流程,進而處理不同的狀況。
( 圖片來源 )
條件判斷並執行決策
程式碼中存在許多狀況需要進行條件判斷,以決定下一步該執行哪一個程式區段,流程圖可以幫助規劃這些情境狀態。
在流程圖中,菱形的狀態代表一個條件判斷點,在此處需要進行條件判斷,並執行決策,此時程式碼便可採取不同路徑流程。
評估條件與條件敘述句
執行決策時,會包含兩個元件:
- 評估條件:對運算式進行評估,並回傳一個結果值。
- 條件敘述句:需描述在該情況下應該執行的工作,是基於 if / then / else 的概念。
if (score > 50) {
document.write('You passed!');
} else {
document.write('Try again...');
}
以上述程式碼的意思,如果條件判斷為 true ,執行第一組大括號裡的條件敘述句,反之執行第二組,之後也會學到透過兩個( 或以上 )的比較運算子,將多個條件結合。
比較運算子:評估條件
可以將程式碼中的值與你的預期值比較評估,評估後的結果會以布林值表示: true 或 false 。
比較運算子 | 名稱 | 說明 | 範例 |
---|---|---|---|
== | 等於 | 比較兩個值( 數字、字串或布林值 )確認它們是否相同。 | 'Hello' == 'Goodbye' // false |
!= | 不等於 | 比較兩個值( 數字、字串或布林值 )確認它們是否不相同。 | 'Hello' != 'Goodbye' // true |
=== | 嚴格等於 | 比較兩個值,以確認資料型別和值均相同。 | '3' === 3 // false |
!== | 嚴格不等於 | 比較兩個值,以確認資料型別和值均不相同。 | '3' !== 3 // true |
> | 大於 | 檢視左邊的數字是否大於右邊的。 | 4 > 3 // true |
< | 大於 | 檢視左邊的數字是否小於右邊的。 | 4 < 3 // false |
>= | 大於等於 | 檢視左邊的數字是否大於或等於右邊的。 | 4 >= 3 // true |
<= | 大於等於 | 檢視左邊的數字是否小於或等於右邊的。 | 4 <= 3 // false |
比較運算子組成結構
在任何一種條件下,比較運算子的結構通常會是一個運算子和兩個運算元,運算元會在運算子的兩側,它們可以是值或變數,然後通常運算式前後會以括號標示。
- 比較運算子。
- 運算元。
- 括號標示。
( 運算元不一定要是單一值或是變數名稱,也可以是一個運算式 )
邏輯運算子
比較運算子通常回傳 true 或 false 的單一結果值,邏輯運算子則可讓你針對多個比較運算子產生的結果值,再進行邏輯判斷,而邏輯運算子是由左至右執行評估,所以如果第一個條件可以提供足夠的資訊來取得答案,那麼就不需要再評估第二個條件了。
邏輯運算子 | 名稱 | 說明 | 範例 |
---|---|---|---|
&& | AND | 測試多個條件是否成立。 - true && true 為 true true && false 為 false false && true 為 false false && false 為 false |
((2 < 5) && (3 >= 2)) // true |
|| | OR | 測試至少一個條件。 - true || true 為 true true || false 為 true false || true 為 true false || false 為 false |
((2 < 5) || (2 < 1)) // true |
! | NOT | 將一個布林值轉換成相反值。 - !true 為 false !false 為 true |
!(2 < 1) // true |
if 條件判斷式
if 條件判斷式會評估( 或檢測 )一個條件,如果這個條件經評估後為 true ,則位於對應程式碼區塊裡的敘述句均會被執行。
if (score >= 50) {
congratulate();
}
如上面的範例,如果條件評估為 true ,便會執行大括號裡的 congratulate(); 程式碼,反之則不會。
if...else 條件判斷式
if...else 條件判斷式會檢測一個條件,如果評估後為 true ,則執行第一個程式碼區塊,反之執行第二個程式碼區塊。
if (score >= 50) {
// 當評估值為true時執行
congratulate();
}
else {
// 當評估值為false時執行
encourage();
}
switch 條件判斷式
switch 條件判斷式是由一個稱為 switch 值的變數展開,每個案例均對應一個可能發生的變數值,及應執行的程式碼區塊。
switch (level) {
case 'One':
title = 'Level 1';
break;
case 'Two':
title = 'Level 2';
break;
default:
title = 'Test';
break;
}
如上面的範例,名稱為 level 的變數就是 switch 值,如果這個 level 變數的值是一個字串 One ,那麼第一個案例的程式碼將被執行;如果是字串 Two ,那麼第二個案例將被執行,如果以上兩種都不是,則執行 default 的程式碼。
整個條件判斷會置於一個大括號裡,其中冒號可將案例條件與該案例對應的程式碼分隔,每個案例最後會以 break 關鍵字結束,它告訴 JavaScript 解譯器 switch 條件判斷句已經結束,並繼續執行後續的程式碼。
if...else vs. switch
if...else
- 可以只使用 if 條件判斷句,並不一定需要提供 else 選項。
- 在一連串的 if 條件判斷句中,即使已找到符合的條件,解譯器還是會檢視全部的條件,因此執行效率會比 switch 敘述句差。
switch
- 如果沒有符合的案例條件,將會執行預設選項。
- 如果有符合的案例條件,便會執行對應的程式碼區塊,接著 break 敘述會停止執行 switch 條件判斷敘述裡其他的指令,相較於多個 if 條件判斷敘述,可提供更好的效能。
型別轉換與弱型別
若你使用 JavaScript 無法理解的資料型別,它會試著將運算合理化,而非直接回報錯誤。
JavaScript 可以在背景將資料型別轉換,以完成一個運算,這個稱之為型別轉換( type coercion ),舉例來說:字串 '1' 在下列運算式中會被轉換為數值 1:('1'>0) ,因此會被評估為 true 。型別轉換會導致程式碼裡有無法預期的值( 也會引起錯誤 ),所以如果要檢查兩個值是否相等,一般認為使用 === 跟 !== 嚴格運算子更適合。
JavaScript 使用的是弱型別( weak typing ),這表示值的資料型別是可以改變的,有些程式語言會要求為每個變數定義資料型別,這就是所謂的強型別( strong typing )。
資料型別 | 目的 |
---|---|
string | 文字。 |
number | 數值。 |
Boolean | true 或 false 。 |
null | 空白值。 |
underfined | 已宣告變數,但尚未指定值。 |
NaN 會被視為數值,當預期的是數值,但並未回傳數值時,可能就會看到 NaN ,例如: ('ten'/2) 。
真與假值
因為型別轉換,每一個在 JavaScript 裡的值都可以被視為真( truthy )或假( falsy )值。
假( falsy )值會被視為 false ,下方標格表示顯示附有一系列不同內容值的 highScore 變數,它們全都是假值,而假值也可以被視為數值 0 。
值 | 說明 |
---|---|
var highScore = false; | 傳統布林值 false 。 |
var highScore = 0; | 數字 0 。 |
var highScore = ''; | 空白值。 |
var highScore = 10/'score'; | NaN ( 不是一個數字 )。 |
var highScore; | 一個未被指定值的變數。 |
真( truthy )值會被視為 true ,幾乎所有沒有放在假值表格中的值,都可以視為是 true ,然後真值也可以被視為數值 1 ,除此之外,物件跟陣列也通常被視為真值,這樣的特質常可使用來檢視頁面上的元件是否存在。
值 | 說明 |
---|---|
var highScore = true; | 傳統布林值 true 。 |
var highScore = 1; | 除了 0 之外的數字。 |
var highScore = 'carrot'; | 有內容的字串。 |
var highScore = 10/5; | 數字計算。 |
var highScore = 'true'; | 'true' 字串。 |
var highScore = '0'; | '0' 字串。 |
var highScore = 'false'; | 'false' 字串。 |
檢測相等性與存在性
因為一個物件和陣列的存在可以被視為真值,這通常可用於檢測元件是否存在於此頁面中。
if (document.getElementByID('header')) {
// 元件存在的話執行
} else {
// 元件不存在的話執行
}
一個一元( unary )運算子表示只需要一個運算元便可以進行運算,並回傳結果值,在上面程式碼中可以看到,一個 if 條件判斷句正檢測頁面中是否有指定的元件存在。
JavaScript 初學者經常會認為以下敘述也有相同效果
if (document.getElementByID('header') == true)
但是這個會回傳一個物件,物件會被視為真值,但並不等同於布林值的 true 。
因為型別轉換, === 和 !== 嚴格運算子比 == 和 != 運算子相較起來,會發生無法預期的值的機率更少。
運算式 | 結果 |
---|---|
(false == 0) | true |
(false === 0) | false |
(false == '') | true |
(false === '') | false |
(0 == '') | true |
(0 === '') | false |
(undefined == null) | true |
(null == false) | false |
(undefined == false) | false |
(null == 0) | false |
(undefined == 0) | false |
(undefined === null) | false |
(NaN == null) | false |
(NaN == NaN) | false |
雖然 NaN 會被視為假值,它與任何值都不相等,甚至連它自己都不相等( 因為 NaN 是個未定義的數值,兩個未定義的數值無法相等 )。
捷徑值
邏輯運算子是由左到右的運算過程,一旦它們取得結果值後,便會採取捷徑方式運算( 也就是停止運算 ),但是它們會回傳讓運算停止的結果值( 不一定是 true 或 false )。
valueA = 0;
valueB = 1;
valueC = 2;
if (valueA || valueB || valueC) {
// 此技巧可以用於檢測頁面上的元件是否存在
}
上方範例當程式碼在邏輯運算子中遇到 valueB 變數時,它將會採用捷徑方式運算,因為數值 1 會被視為真值,只要在邏輯運算中發現真值,其餘的選項將不會被檢測,因此有經驗的程式設計師通常會:
- 在 or 運算時,把最有可能回傳真值的程式碼置於最前方;而在 and 運算時,將最有可能回傳假值的程式碼置於最前方。
- 將需花費最多運算能量的選項置於最後方,如此只要有其他回傳值為真值,該選項就不需要被執行。
迴圈
迴圈( loops )會檢測條件,如果回傳為 true ,便會運行程式碼區塊,接著條件將被再度檢測,如果它的回傳值仍為 true ,程式碼區塊將再度運行,它會重複檢測直到條件為 false ,以下是三種常見的迴圈類型:
迴圈類型 | 說明 |
---|---|
for | 如果你需要指定運行程式碼的次數,就必須要使用 for 迴圈,通常以計數器作為條件,以決定迴圈要運行的次數。 |
while | 當你並不知道程式碼必須運行的次數時,就可以使用 while 迴圈,在這種狀況下,條件不具有計數器的功能,但只要條件的回傳值為 true ,迴圈便會持續運行。 |
do while | do...while 迴圈和 while 迴圈非常類似,最主要的不同在於它會運行大括號裡的程式敘述句至少一次,即使條件評估為 false 。 |
迴圈計數器
for 迴圈使用計數器為條件,這樣可以指引程式碼運行特定的次數,而條件是由三個描述句組成的:
初始化
建立一個變數並設定為 0 ,這個變數通常命名為 i ( 有些會命名 index ),並將它用於計數器的功能。
var i = 0;
有時會看到變數被宣告於條件前,如下範例,看每個程式設計師的撰寫習慣:
var i;
for (i = 0; i < 10; i++) {
}
條件
迴圈持續執行,直到計數器達到指定次數。
i < 10;
// i的初始值設定為0,所以此範例中,迴圈將運行十次後停止
條件也可能使用變數,如果有個名為 rounds 的變數,此變數保存著測驗的次數,且每次迴圈運行一次就執行一次測驗,那麼條件將會如下:
var rounds = 3;
i < (rounds);
更新
每次迴圈執行完大括號裡的敘述句時,若要於計數器上增加一次計數,可使用遞增運算子( ++ );反之則用遞減運算子( -- )。
i++
// 擷取變數i,並使用++運算子將變數i增加1
循環執行
迴圈會按照下面的順序執行:
for (var i = 0; i < 10; i++) {
document.write(i);
}
- 第一次迴圈執行時,變數 i ( 計數器 )被指定為數值 0 。
- 每次迴圈執行時,均檢測條件是否符合,變數 i 是否小於 10 。
- 若符合條件,接著便會執行迴圈裡的程式碼( 大括號裡的敘述句 )。
- 當程式敘述句執行完畢後,變數 i 會加一。
- 當條件不再為 true 時,迴圈將會停止,程式碼會移動到迴圈外的下一行。
迴圈重要概念
以下三個要點是使用迴圈時需要注意的:
關鍵字
迴圈中會很常看到這兩個關鍵字 break
會終止迴圈執行,並告訴解譯器移動到迴圈外的下一個敘述句程式碼;而 continue
則告訴解譯器繼續目前的迴圈,並再度檢測條件( 如果條件為 true 便再次運行程式碼 )。
迴圈與陣列
如果想要對陣列裡的每個項目套用相同的程式碼,迴圈對於陣列的資料處理是非常有效的。
效能議題
當瀏覽器遇到 JavaScript 時,它會執行所有正在進行的工作,直到它將程式碼執行完畢,正是如此,如果迴圈包含許多資料項目就會導致頁面下載速度變慢,然後如果條件永遠不回傳 false 值,這會成為一個無限迴圈,程式碼將不會停止運行直到你的瀏覽器記憶體不足為止。
運用 for 迴圈
for 迴圈有宣告索引變數,也因為宣告了索引變數,所以可以記錄目前跑了幾次迴圈,迴圈次數到了就可以跳出迴圈了。
for (初始值; 條件; 結束時的變動) {
// 你想重複執行的動作
}
運用 while 迴圈
while 迴圈不須宣告索引變數,只要執行條件仍然滿足,就可以一直迴圈下去。
while (執行條件) {
重複執行的程式們
}
但其實 while 迴圈也可達成 for 迴圈的任務,只要自行在「 迴圈外 」宣告索引變數,並在「 迴圈內 」寫入每次迭代時索引變數的變化,就可達到同樣的效果,如下:
let i = 1
while (i <= 10) {
重複執行的程式們
i++
}
for 跟 while 迴圈差在哪?
當迭代次數已知,也就是當你知道要跑幾次迴圈時,可使用 for 迴圈;而當只知道迴圈執行條件,還不清楚總共需跑幾次迴圈時,就只能使用 while 迴圈。
兩種寫法主要的差異在於:
- 索引變數的有效範圍: for 迴圈的索引變數只在迴圈內有效,而 while 的則在圈外也有效。
- 索引變數變化的位置: for 迴圈一律在進入迴圈前;而 while 則可任意選擇其在迴圈內的位置。
運用 do while 迴圈
while 和 do while 的語法非常像, while 是會檢查條件是否成立,成立才執行下面的指令,而 do while 則是先執行那些指令,再去檢查條件是否成立,所以至少會先執行一次。
do {
執行動作
} while (條件);
下面範例的 do while 迴圈將會印出 1 次 test ,由此可見此迴圈至少一定會執行 1 次。
int y = 1;
do {
Response.write("test");
y++;
} while(y < 1);
下面範例的 while 迴圈將不會印出任何文字。
int y = 1;
while (y < 1) {
Response.write("test");
y++;
}
參考資料
- JavaScript & jQuery 網站互動設計程式進化之道( Jon Duckett 著、謝銘倫 譯 )
- 神之迴圈: 何時用While? 何時用For?
- JavaScript基本功修練:Day10 - for、for與while的差別、do while、break、continue
- 網頁設計及程式設計教學:while 與 do-while 迴圈