控制流和错误处理
JavaScript 支持一组紧凑的语句,特别是控制流语句,您可以使用它们在应用程序中加入大量交互性。本章概述了这些语句。
该 JavaScript 参考 包含本章中语句的详尽细节。分号 (;
) 字符用于分隔 JavaScript 代码中的语句。
任何 JavaScript 表达式也是一个语句。请参阅 表达式和运算符 获取有关表达式的完整信息。
块语句
最基本的语句是块语句,用于对语句进行分组。块由一对花括号分隔
{
statement1;
statement2;
// …
statementN;
}
示例
条件语句
条件语句是一组命令,如果指定条件为真,则执行这些命令。JavaScript 支持两种条件语句:if...else
和 switch
。
if...else 语句
使用 if
语句在逻辑条件为 true
时执行语句。使用可选的 else
子句在条件为 false
时执行语句。
if
语句如下所示
if (condition) {
statement1;
} else {
statement2;
}
这里,condition
可以是任何计算结果为 true
或 false
的表达式。(请参阅 布尔值 以了解哪些值计算结果为 true
和 false
。)
如果 condition
计算结果为 true
,则执行 statement_1
。否则,执行 statement_2
。statement_1
和 statement_2
可以是任何语句,包括进一步嵌套的 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
文档有一个 使用赋值作为条件 部分,其中包含有关您应该了解并遵循的一般最佳实践语法的指南。
假值
以下值计算结果为 false
(也称为 假值):
false
undefined
null
0
NaN
- 空字符串 (
""
)
所有其他值(包括所有对象)在传递给条件语句时计算结果都为 true
。
注意: 不要将原始布尔值 true
和 false
与 Boolean
对象的真值和假值混淆!
例如
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;
} else {
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 语句:
- 程序首先查找标签与 expression 值匹配的
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("Mangoes and 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
) 不对应,则会抛出异常,其值为 'InvalidMonthNo'
,并且 catch
块中的语句将 monthName
变量设置为 'unknown'
。
function getMonthName(mo) {
mo--; // Adjust month number for array index (so that 0 = Jan, 11 = Dec)
const months = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
];
if (months[mo]) {
return months[mo];
} else {
throw new Error("InvalidMonthNo"); // throw keyword is used here
}
}
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
语句指定的值。您可以使用此标识符来获取有关已抛出异常的信息。
当进入catch
块时,JavaScript 会创建此标识符。该标识符仅在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
块中的任何return
语句如何,此值都将成为整个try…catch…finally
结构的返回值。
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"
console.log(4); // not reachable
}
// "return false" is executed now
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"
}
// "return false" is executed now
}
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 块在try...catch
参考页面上。
利用 Error 对象
根据错误类型,您可能能够使用name
和message
属性来获取更详细的消息。
name
属性提供Error
的一般类(例如DOMException
或Error
),而message
通常提供比将错误对象转换为字符串获得的更简洁的消息。
如果您正在抛出自己的异常,为了利用这些属性(例如,如果您的catch
块不区分您自己的异常和系统异常),您可以使用Error
构造函数。
例如
function doSomethingErrorProne() {
if (ourCodeMakesAMistake()) {
throw new Error("The message");
} else {
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
}