控制流程和错误处理
JavaScript 支持一组紧凑的语句,特别是控制流语句,你可以使用它们在应用中合并大量的交互性。本章概述了这些陈述。
¥JavaScript supports a compact set of statements, specifically control flow statements, that you can use to incorporate a great deal of interactivity in your application. This chapter provides an overview of these statements.
JavaScript 参考 包含有关本章中的陈述的详尽细节。分号 (;) 字符用于分隔 JavaScript 代码中的语句。
¥The JavaScript reference
contains exhaustive details about the statements in this chapter. The semicolon
(;) character is used to separate statements in JavaScript code.
任何 JavaScript 表达式也是一个语句。有关表达式的完整信息,请参阅 表达式和运算符。
¥Any JavaScript expression is also a statement. See Expressions and operators for complete information about expressions.
块语句
示例
¥Example
块语句通常与控制流语句(if、for、while)一起使用。
¥Block statements are commonly used with control flow statements (if,
for, while).
while (x < 10) {
x++;
}
这里,{ x++; } 是块语句。
¥Here, { x++; } is the block statement.
注意:
var声明的变量不是块作用域的,但作用域是包含函数或脚本的,并且设置它们的效果在块本身之外仍然存在。例如:¥Note:
var-declared variables are not block-scoped, but are scoped to the containing function or script, and the effects of setting them persist beyond the block itself. For example:jsvar x = 1; { var x = 2; } console.log(x); // 2这会输出
2,因为该块内的var x语句与该块之前的var x语句位于同一范围内。(在 C 或 Java 中,等效代码将输出1。)¥This outputs
2because thevar xstatement within the block is in the same scope as thevar xstatement before the block. (In C or Java, the equivalent code would have output1.)¥This scoping effect can be mitigated by using
letorconst.
条件语句
if...else 语句
¥if...else statement
如果逻辑条件为 true,则使用 if 语句来执行语句。如果条件为 false,则使用可选的 else 子句执行语句。
¥Use the if statement to execute a statement if a logical condition is
true. Use the optional else clause to execute a statement if
the condition is false.
if 语句如下所示:
¥An if statement looks like this:
if (condition) {
statement1;
} else {
statement2;
}
这里,condition 可以是任何计算结果为 true 或 false 的表达式。(有关 true 和 false 的计算结果的说明,请参阅 布尔值。)
¥Here, the condition can be any expression that evaluates to
true or false. (See Boolean
for an explanation of what evaluates to true and false.)
如果 condition 的计算结果为 true,则执行 statement_1。否则,执行 statement_2。statement_1 和 statement_2 可以是任何语句,包括进一步嵌套的 if 语句。
¥If condition evaluates to true,
statement_1 is executed. Otherwise,
statement_2 is executed. statement_1 and
statement_2 can be any statement, including further nested
if statements.
你还可以使用 else if 组合语句以按顺序测试多个条件,如下所示:
¥You can also compound the statements using else if to have multiple
conditions tested in sequence, as follows:
if (condition1) {
statement1;
} else if (condition2) {
statement2;
} else if (conditionN) {
statementN;
} else {
statementLast;
}
如果有多个条件,则仅执行第一个值为 true 的逻辑条件。要执行多个语句,请将它们分组在块语句 ({ /* … */ }) 中。
¥In the case of multiple conditions, only the first logical condition which evaluates to
true will be executed. To execute multiple statements, group them within a
block statement ({ /* … */ }).
最佳实践
¥Best practice
一般来说,最好始终使用块语句,尤其是在嵌套 if 语句时:
¥In general, it's good practice to always use block statements—especially when
nesting if statements:
if (condition) {
// Statements for when condition is true
// …
} else {
// Statements for when condition is false
// …
}
一般来说,最好不要将 if...else 与 x = y 这样的作业作为条件:
¥In general it's good practice to not have an if...else with an assignment like x = y as a condition:
if (x = y) {
// statements here
}
然而,在极少数情况下,你发现自己想要做类似的事情,while 文档有一个 使用赋值作为条件 部分,其中提供了你应该了解并遵循的一般最佳实践语法的指导。
¥However, in the rare case you find yourself wanting to do something like that, the while documentation has a Using an assignment as a condition section with guidance on a general best-practice syntax you should know about and follow.
错误的价值观
¥Falsy values
以下值计算为 false(也称为 假值 值):
¥The following values evaluate to false (also known as Falsy values):
falseundefinednull0NaN- 空字符串 (
"")
所有其他值(包括所有对象)在传递给条件语句时计算结果为 true。
¥All other values—including all objects—evaluate to true when passed to a
conditional statement.
注意:不要将原始布尔值
true和false与Boolean对象的 true 和 false 值混淆!¥Note: Do not confuse the primitive boolean values
trueandfalsewith the true and false values of theBooleanobject!例如:
¥For example:
jsconst b = new Boolean(false); if (b) { // this condition evaluates to true } if (b == true) { // this condition evaluates to false }
示例
¥Example
在以下示例中,如果 Text 对象中的字符数为 3,则函数 checkData 返回 true。否则,它会显示警报并返回 false。
¥In the following example, the function checkData returns true
if the number of characters in a Text object is three. Otherwise, it
displays an alert and returns 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 statement
switch 语句允许程序计算表达式并尝试将表达式的值与 case 标签相匹配。如果找到匹配项,程序将执行关联的语句。
¥A switch statement allows a program to evaluate an expression and attempt
to match the expression's value to a case label. If a match is found, the
program executes the associated statement.
switch 语句如下所示:
¥A switch statement looks like this:
switch (expression) {
case label1:
statements1;
break;
case label2:
statements2;
break;
// …
default:
statementsDefault;
}
JavaScript 按如下方式评估上述 switch 语句:
¥JavaScript evaluates the above switch statement as follows:
- 程序首先查找标签与表达式值匹配的
case子句,然后将控制权转移到该子句,执行关联的语句。 - 如果没有找到匹配的标签,程序将查找可选的
default子句:- 如果找到
default子句,程序会将控制权转移到该子句,并执行关联的语句。 - 如果没有找到
default子句,则程序从switch末尾后面的语句处恢复执行。 - (按照惯例,
default子句写为最后一个子句,但不一定如此。)
- 如果找到
中断语句
¥break statements
与每个 case 子句关联的可选 break 语句确保一旦执行匹配的语句,程序就会跳出 switch,然后继续执行 switch 后面的语句。如果省略 break,程序将继续执行 switch 语句内的语句(并将执行下一个 case 下的语句,依此类推)。
¥The optional break statement associated with each case clause
ensures that the program breaks out of switch once the matched statement is
executed, and then continues execution at the statement following switch.
If break is omitted, the program continues execution inside the
switch statement (and will execute statements under the next case, and so on).
示例
¥Example
在以下示例中,如果 fruitType 的计算结果为 'Bananas',则程序将该值与 case 'Bananas' 相匹配并执行关联的语句。当遇到 break 时,程序退出 switch 并从 switch 后面的语句继续执行。如果省略 break,则 case 'Cherries' 的语句也会被执行。
¥In the following example, if fruitType evaluates to
'Bananas', the program matches the value with case 'Bananas'
and executes the associated statement. When break is encountered, the
program exits the switch and continues execution from the statement
following switch. If break were omitted, the statement for
case 'Cherries' would also be executed.
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?");
异常处理语句
¥Exception handling statements
你可以使用 throw 语句引发异常并使用 try...catch 语句处理异常。
¥You can throw exceptions using the throw statement and handle them using
the try...catch statements.
异常类型
¥Exception types
几乎任何对象都可以在 JavaScript 中抛出。然而,并非所有抛出的对象都是一样的。虽然将数字或字符串作为错误抛出是很常见的,但使用专门为此目的创建的异常类型之一通常更有效:
¥Just about any object can be thrown in JavaScript. Nevertheless, not all thrown objects are created equal. While it is common to throw numbers or strings as errors, it is frequently more effective to use one of the exception types specifically created for this purpose:
抛出语句
¥throw statement
使用 throw 语句抛出异常。throw 语句指定要抛出的值:
¥Use the throw statement to throw an exception. A throw
statement specifies the value to be thrown:
throw expression;
你可以抛出任何表达式,而不仅仅是特定类型的表达式。以下代码抛出多个不同类型的异常:
¥You may throw any expression, not just expressions of a specific type. The following code throws several exceptions of varying types:
throw "Error2"; // String type
throw 42; // Number type
throw true; // Boolean type
throw {
toString() {
return "I'm an object!";
},
};
try...catch 声明
¥try...catch statement
try...catch 语句标记要尝试的语句块,并指定抛出异常时的一个或多个响应。如果抛出异常,try...catch 语句将捕获它。
¥The try...catch statement marks a block of statements to try, and
specifies one or more responses should an exception be thrown. If an exception is
thrown, the try...catch statement catches it.
try...catch 语句由 try 块和 catch 块组成,其中 try 块包含一个或多个语句,而 catch 块包含指定在 try 块中引发异常时要执行的操作的语句。
¥The try...catch statement consists of a try block, which
contains one or more statements, and a catch block, containing statements
that specify what to do if an exception is thrown in the try block.
换句话说,你希望 try 块成功,但如果不成功,你希望控制权传递给 catch 块。如果 try 块内(或从 try 块内调用的函数中)的任何语句引发异常,则控制立即转移到 catch 块。如果 try 块中没有抛出异常,则跳过 catch 块。finally 块在 try 和 catch 块执行之后但在 try...catch 语句后面的语句之前执行。
¥In other words, you want the try block to succeed—but if it does not, you
want control to pass to the catch block. If any statement within the
try block (or in a function called from within the try block)
throws an exception, control immediately shifts to the catch
block. If no exception is thrown in the try block, the catch
block is skipped. The finally block executes after the try and
catch blocks execute but before the statements following the
try...catch statement.
以下示例使用 try...catch 语句。该示例调用一个函数,该函数根据传递给该函数的值从数组中检索月份名称。如果该值与月份数字 (1 – 12) 不对应,则会引发异常,并显示值 'InvalidMonthNo',并且 catch 块中的语句将 monthName 变量设置为 'unknown'。
¥The following example uses a try...catch statement. The example calls a
function that retrieves a month name from an array based on the value passed to the
function. If the value does not correspond to a month number
(1 – 12), an exception is thrown with the value
'InvalidMonthNo' and the statements in the catch block set the
monthName variable to '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)
}
捕获块
¥The catch block
你可以使用 catch 块来处理 try 块中可能生成的所有异常。
¥You can use a catch block to handle all exceptions that may be generated
in the try block.
catch (exception) {
statements
}
catch 块指定一个标识符(前面语法中的 exception),该标识符保存 throw 语句指定的值。你可以使用此标识符来获取有关引发的异常的信息。
¥The catch block specifies an identifier (exception
in the preceding syntax) that holds the value specified by the throw
statement. You can use this identifier to get information about the exception that was
thrown.
当输入 catch 块时,JavaScript 创建此标识符。该标识符仅在 catch 块的持续时间内持续。一旦 catch 块执行完毕,标识符就不再存在。
¥JavaScript creates this identifier when the catch block is entered. The
identifier lasts only for the duration of the catch block. Once the
catch block finishes executing, the identifier no longer exists.
例如,以下代码会引发异常。当异常发生时,控制权转移到 catch 块。
¥For example, the following code throws an exception. When the exception occurs, control
transfers to the catch block.
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()进行调试。它将消息格式化为错误,并将其添加到页面生成的错误消息列表中。¥Note: When logging errors to the console inside a
catchblock, usingconsole.error()rather thanconsole.log()is advised for debugging. It formats the message as an error, and adds it to the list of error messages generated by the page.
最后块
¥The finally block
finally 块包含在 try 和 catch 块执行后要执行的语句。此外,finally 块在 try…catch…finally 语句后面的代码之前执行。
¥The finally block contains statements to be executed after the
try and catch blocks execute. Additionally, the
finally block executes before the code that follows the
try…catch…finally statement.
还需要注意的是,无论是否抛出异常,finally 块都会执行。但是,如果引发异常,即使没有 catch 块处理引发的异常,finally 块中的语句也会执行。
¥It is also important to note that the finally block will execute
whether or not an exception is thrown. If an exception is thrown, however, the
statements in the finally block execute even if no catch block
handles the exception that was thrown.
你可以使用 finally 块使脚本在发生异常时正常失败。例如,你可能需要释放脚本占用的资源。
¥You can use the finally block to make your script fail gracefully when an
exception occurs. For example, you may need to release a resource that your script has
tied up.
以下示例打开一个文件,然后执行使用该文件的语句。(服务器端 JavaScript 允许你访问文件。)如果在文件打开时引发异常,则 finally 块会在脚本失败之前关闭该文件。此处使用 finally 可确保文件永远不会保持打开状态,即使发生错误也是如此。
¥The following example opens a file and then executes statements that use the file.
(Server-side JavaScript allows you to access files.) If an exception is thrown while the
file is open, the finally block closes the file before the script fails.
Using finally here ensures that the file is never left open, even
if an error occurs.
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 语句如何:
¥If the finally block returns a value, this value becomes the return value
of the entire try…catch…finally production, regardless of any
return statements in the try and catch blocks:
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 块内部抛出或重新抛出的异常:
¥Overwriting of return values by the finally block also applies to
exceptions thrown or re-thrown inside of the catch block:
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 语句
¥Nesting try...catch statements
你可以嵌套一个或多个 try...catch 语句。
¥You can nest one or more try...catch statements.
如果内部 try 块没有对应的 catch 块:
¥If an inner try block does not have a corresponding
catch block:
- 它必须包含
finally块,并且 - 检查封闭的
try...catch语句的catch块是否匹配。
有关详细信息,请参阅 try...catch 参考页上的 嵌套的 try 块。
¥For more information, see nested try-blocks
on the try...catch
reference page.
使用错误对象
¥Utilizing Error objects
根据错误的类型,你也许可以使用 name 和 message 属性来获取更精确的消息。
¥Depending on the type of error, you may be able to use the name and
message properties to get a more refined message.
name 属性提供 Error 的通用类(例如 DOMException 或 Error),而 message 通常提供比将错误对象转换为字符串所获得的消息更简洁的消息。
¥The name property provides the general class of Error (such
as DOMException or Error), while message
generally provides a more succinct message than one would get by converting the error
object to a string.
如果你抛出自己的异常,为了利用这些属性(例如,如果你的 catch 块不区分你自己的异常和系统异常),你可以使用 Error 构造函数。
¥If you are throwing your own exceptions, in order to take advantage of these properties
(such as if your catch block doesn't discriminate between your own
exceptions and system ones), you can use the Error constructor.
例如:
¥For example:
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
}