控制流程和错误处理
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
2
because thevar x
statement within the block is in the same scope as thevar x
statement before the block. (In C or Java, the equivalent code would have output1
.)¥This scoping effect can be mitigated by using
let
orconst
.
条件语句
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):
false
undefined
null
0
NaN
- 空字符串 (
""
)
所有其他值(包括所有对象)在传递给条件语句时计算结果为 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
true
andfalse
with the true and false values of theBoolean
object!例如:
¥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
catch
block, 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
}