控制流和錯誤處理
JavaScript 支援一組緊湊的語句,特別是控制流語句,您可以使用它們在應用程式中融入大量互動性。本章概述了這些語句。
JavaScript 參考包含了本章中語句的詳盡細節。分號 (;) 字元用於分隔 JavaScript 程式碼中的語句。
任何 JavaScript 表示式也是一個語句。有關表示式的完整資訊,請參閱表示式與運算子。
塊語句
最基本的語句是“塊語句”,用於將語句分組。塊由一對大括號分隔
{
statement1;
statement2;
// …
statementN;
}
示例
塊語句通常與控制流語句 (if, for, while) 一起使用。
while (x < 10) {
x++;
}
這裡,{ x++; } 是塊語句。
條件語句
條件語句是一組命令,如果指定條件為真,則執行。JavaScript 支援兩種條件語句:if...else 和 switch。
if...else 語句
使用 if 語句在邏輯條件為 true 時執行語句。使用可選的 else 子句在條件為 false 時執行語句。
一個 if 語句看起來像這樣
if (condition) {
statement1;
} else {
statement2;
}
這裡,condition 可以是任何求值為 true 或 false 的表示式。(有關什麼求值為 true 和 false 的解釋,請參閱布林值。)
如果 condition 求值為 true,則執行 statement1。否則,執行 statement2。statement1 和 statement2 可以是任何語句,包括進一步巢狀的 if 語句。
您還可以使用 else if 組合語句,以按順序測試多個條件,如下所示
if (condition1) {
statement1;
} else if (condition2) {
statement2;
} else if (conditionN) {
statementN;
} else {
statementLast;
}
在多個條件的情況下,只執行第一個求值為 true 的邏輯條件。要執行多個語句,請將它們分組在一個塊語句 ({ /* … */ }) 中。
最佳實踐
通常,始終使用塊語句是很好的實踐——“尤其是”在巢狀 if 語句時
if (condition) {
// Statements for when condition is true
// …
} else {
// Statements for when condition is false
// …
}
通常,避免在 if...else 中將賦值(如 x = y)作為條件
if (x = y) {
// statements here
}
但是,在您偶爾發現自己想要這樣做的情況下,while 文件有一個使用賦值作為條件的部分,其中提供了您應該瞭解和遵循的一般最佳實踐語法指南。
Falsy 值
以下值求值為 false(也稱為Falsy 值)
falseundefinednull0NaN- 空字串 (
"")
所有其他值——包括所有物件——在傳遞給條件語句時都求值為 true。
注意: 不要將原始布林值 true 和 false 與 Boolean 物件的 true 和 false 值混淆!
例如
const b = new Boolean(false);
if (b) {
// this condition evaluates to true
}
if (b == true) {
// this condition evaluates to false
}
示例
在以下示例中,如果 Text 物件中的字元數為三,則函式 checkData 返回 true。否則,它會顯示一個警告並返回 false。
function checkData() {
if (document.form1.threeChar.value.length === 3) {
return true;
}
alert(
`Enter exactly three characters. ${document.form1.threeChar.value} is not valid.`,
);
return false;
}
switch 語句
switch 語句允許程式評估一個表示式,並嘗試將其值與 case 標籤匹配。如果找到匹配項,程式將執行關聯的語句。
一個 switch 語句看起來像這樣
switch (expression) {
case label1:
statements1;
break;
case label2:
statements2;
break;
// …
default:
statementsDefault;
}
JavaScript 按如下方式評估上述 switch 語句
- 程式首先尋找一個標籤與表示式值匹配的
case子句,然後將控制權轉移到該子句,執行關聯的語句。 - 如果沒有找到匹配的標籤,程式將尋找可選的
default子句- 如果找到
default子句,程式將控制權轉移到該子句,執行關聯的語句。 - 如果未找到
default子句,程式將從switch結束後的語句處恢復執行。 - (按照慣例,
default子句被寫為最後一個子句,但它不必如此。)
- 如果找到
break 語句
與每個 case 子句關聯的可選 break 語句確保程式在執行匹配的語句後跳出 switch,然後從 switch 後的語句處繼續執行。如果省略 break,程式將繼續在 switch 語句內部執行(並將執行下一個 case 下的語句,依此類推)。
示例
在以下示例中,如果 fruitType 求值為 "Bananas",程式會將該值與 case "Bananas" 匹配並執行關聯的語句。當遇到 break 時,程式退出 switch 並從 switch 後的語句處繼續執行。如果省略 break,則 case "Cherries" 的語句也將被執行。
switch (fruitType) {
case "Oranges":
console.log("Oranges are $0.59 a pound.");
break;
case "Apples":
console.log("Apples are $0.32 a pound.");
break;
case "Bananas":
console.log("Bananas are $0.48 a pound.");
break;
case "Cherries":
console.log("Cherries are $3.00 a pound.");
break;
case "Mangoes":
console.log("Mangoes are $0.56 a pound.");
break;
case "Papayas":
console.log("Papayas are $2.79 a pound.");
break;
default:
console.log(`Sorry, we are out of ${fruitType}.`);
}
console.log("Is there anything else you'd like?");
異常處理語句
您可以使用 throw 語句丟擲異常,並使用 try...catch 語句處理它們。
異常型別
幾乎任何物件都可以在 JavaScript 中丟擲。然而,並非所有丟擲的物件都是平等的。雖然通常將數字或字串作為錯誤丟擲,但使用為此目的專門建立的異常型別通常更有效
throw 語句
使用 throw 語句丟擲異常。throw 語句指定要丟擲的值
throw expression;
您可以丟擲任何表示式,而不僅僅是特定型別的表示式。以下程式碼丟擲了幾種不同型別的異常
throw "Error2"; // String type
throw 42; // Number type
throw true; // Boolean type
throw {
toString() {
return "I'm an object!";
},
};
try...catch 語句
try...catch 語句標記一個要嘗試的語句塊,並指定在丟擲異常時的一個或多個響應。如果丟擲異常,try...catch 語句會捕獲它。
try...catch 語句由一個 try 塊組成,其中包含一個或多個語句,以及一個 catch 塊,其中包含在 try 塊中丟擲異常時要執行的操作的語句。
換句話說,您希望 try 塊成功——但如果它不成功,您希望控制權傳遞給 catch 塊。如果在 try 塊內(或從 try 塊內呼叫的函式中)的任何語句丟擲異常,控制權會“立即”轉移到 catch 塊。如果在 try 塊中沒有丟擲異常,則 catch 塊被跳過。finally 塊在 try 和 catch 塊執行之後,但在 try...catch 語句後面的語句之前執行。
以下示例使用 try...catch 語句。該示例呼叫一個函式,該函式根據傳遞給函式的值從陣列中檢索月份名稱。如果該值與月份編號 (1 – 12) 不對應,則會丟擲值為 'Invalid month code' 的異常,並且 catch 塊中的語句將 monthName 變數設定為 'unknown'。
function getMonthName(mo) {
mo--; // Adjust month number for array index (so that 0 = Jan, 11 = Dec)
// prettier-ignore
const months = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
];
if (!months[mo]) {
throw new Error("Invalid month code"); // throw keyword is used here
}
return months[mo];
}
try {
// statements to try
monthName = getMonthName(myMonth); // function could throw exception
} catch (e) {
monthName = "unknown";
logMyErrors(e); // pass exception object to error handler (i.e. your own function)
}
catch 塊
您可以使用 catch 塊處理 try 塊中可能生成的所有異常。
catch (exception) {
statements
}
catch 塊指定一個識別符號(在前面的語法中為 exception),該識別符號儲存由 throw 語句指定的值。您可以使用此識別符號獲取有關丟擲異常的資訊。
JavaScript 在進入 catch 塊時建立此識別符號。該識別符號僅在 catch 塊的持續時間內有效。一旦 catch 塊執行完畢,該識別符號就不再存在。
例如,以下程式碼丟擲異常。當異常發生時,控制權轉移到 catch 塊。
try {
throw "myException"; // generates an exception
} catch (err) {
// statements to handle any exceptions
logMyErrors(err); // pass exception object to error handler
}
注意: 在 catch 塊內將錯誤記錄到控制檯時,建議使用 console.error() 而不是 console.log() 進行除錯。它將訊息格式化為錯誤,並將其新增到頁面生成的錯誤訊息列表中。
finally 塊
finally 塊包含在 try 和 catch 塊執行“之後”執行的語句。此外,finally 塊在 try...catch...finally 語句後面的程式碼“之前”執行。
同樣重要的是要注意,finally 塊將“無論是否”丟擲異常都會執行。但是,如果丟擲異常,即使沒有 catch 塊處理丟擲的異常,finally 塊中的語句也會執行。
您可以使用 finally 塊在發生異常時使指令碼優雅地失敗。例如,您可能需要釋放指令碼佔用的資源。
以下示例開啟一個檔案,然後執行使用該檔案的語句。(伺服器端 JavaScript 允許您訪問檔案。)如果在檔案開啟時丟擲異常,finally 塊會在指令碼失敗之前關閉檔案。在此處使用 finally“確保”即使發生錯誤,檔案也永遠不會保持開啟狀態。
openMyFile();
try {
writeMyFile(theData); // This may throw an error
} catch (e) {
handleError(e); // If an error occurred, handle it
} finally {
closeMyFile(); // Always close the resource
}
如果 finally 塊返回一個值,則該值將成為整個 try...catch...finally 結構的返回值,無論 try 和 catch 塊中是否存在任何 return 語句
function f() {
try {
console.log(0);
throw "bogus";
} catch (e) {
console.log(1);
// This return statement is suspended
// until finally block has completed
return true;
console.log(2); // not reachable
} finally {
console.log(3);
return false; // overwrites the previous "return"
// `f` exits here
console.log(4); // not reachable
}
console.log(5); // not reachable
}
console.log(f()); // 0, 1, 3, false
finally 塊對返回值的覆蓋也適用於在 catch 塊內丟擲或重新丟擲的異常
function f() {
try {
throw "bogus";
} catch (e) {
console.log('caught inner "bogus"');
// This throw statement is suspended until
// finally block has completed
throw e;
} finally {
return false; // overwrites the previous "throw"
// `f` exits here
}
}
try {
console.log(f());
} catch (e) {
// this is never reached!
// while f() executes, the `finally` block returns false,
// which overwrites the `throw` inside the above `catch`
console.log('caught outer "bogus"');
}
// Logs:
// caught inner "bogus"
// false
巢狀 try...catch 語句
您可以巢狀一個或多個 try...catch 語句。
如果內部 try 塊“沒有”相應的 catch 塊
- 它“必須”包含一個
finally塊,並且 - 將檢查包含
try...catch語句的catch塊是否匹配。
有關更多資訊,請參閱 try...catch 參考頁面上的巢狀 try 塊。
使用 Error 物件
根據錯誤的型別,您可以使用 name 和 message 屬性來獲取更精確的訊息。
name 屬性提供 Error 的一般類別(如 DOMException 或 Error),而 message 通常提供比將錯誤物件轉換為字串時更簡潔的訊息。
如果您正在丟擲自己的異常,為了利用這些屬性(例如,如果您的 catch 塊不區分您自己的異常和系統異常),您可以使用 Error 建構函式。
例如
function doSomethingErrorProne() {
if (ourCodeMakesAMistake()) {
throw new Error("The message");
}
doSomethingToGetAJavaScriptError();
}
try {
doSomethingErrorProne();
} catch (e) {
// Now, we actually use `console.error()`
console.error(e.name); // 'Error'
console.error(e.message); // 'The message', or a JavaScript error message
}