控制流和错误处理
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
}