try...catch
try...catch
语句由一个 try
块和一个 catch
块、一个 finally
块或两者组成。try
块中的代码首先执行,如果它抛出异常,catch
块中的代码将被执行。finally
块中的代码将在控制流退出整个构造之前始终执行。
尝试
语法
try {
tryStatements
} catch (exceptionVar) {
catchStatements
} finally {
finallyStatements
}
tryStatements
-
要执行的语句。
catchStatements
-
如果
try
块中抛出异常,则执行的语句。 exceptionVar
可选-
一个可选的 标识符或模式,用于保存与关联
catch
块捕获的异常。如果catch
块不使用异常的值,可以省略exceptionVar
及其周围的括号。 finallyStatements
-
在控制流退出
try...catch...finally
构造之前执行的语句。这些语句无论是否抛出或捕获异常都会执行。
描述
try
语句始终以一个 try
块开头。然后,必须存在一个 catch
块或一个 finally
块。也可以同时具有 catch
和 finally
块。这给了我们 try
语句的三种形式
try...catch
try...finally
try...catch...finally
与其他构造(如 if
或 for
)不同,try
、catch
和 finally
块必须是块,而不是单个语句。
try doSomething(); // SyntaxError
catch (e) console.log(e);
catch
块包含指定如果 try
块中抛出异常该怎么办的语句。如果 try
块中的任何语句(或在从 try
块中调用的函数中)抛出异常,控制流将立即转移到 catch
块。如果 try
块中没有抛出异常,则 catch
块将被跳过。
finally
块将在控制流退出 try...catch...finally
构造之前始终执行。它始终执行,无论是否抛出或捕获异常。
可以嵌套一个或多个 try
语句。如果内部 try
语句没有 catch
块,则使用封闭 try
语句的 catch
块。
还可以使用 try
语句来处理 JavaScript 异常。有关 JavaScript 异常的更多信息,请参阅 JavaScript 指南。
捕获绑定
当 try
块中抛出异常时,exceptionVar
(即 catch (e)
中的 e
)将保存异常值。可以使用此 绑定 来获取有关抛出异常的信息。此 绑定 仅在 catch
块的 作用域 中可用。
它不需要是单个标识符。可以使用 解构模式 来一次分配多个标识符。
try {
throw new TypeError("oops");
} catch ({ name, message }) {
console.log(name); // "TypeError"
console.log(message); // "oops"
}
由 catch
子句创建的绑定与 catch
块位于相同的范围,因此在 catch
块中声明的任何变量不能与由 catch
子句创建的绑定具有相同的名称。(对此规则有一个 例外,但它是已弃用的语法。)
try {
throw new TypeError("oops");
} catch ({ name, message }) {
var name; // SyntaxError: Identifier 'name' has already been declared
let message; // SyntaxError: Identifier 'message' has already been declared
}
异常绑定是可写的。例如,可能需要规范化异常值以确保它是一个 Error
对象。
try {
throw "Oops; this is not an Error object";
} catch (e) {
if (!(e instanceof Error)) {
e = new Error(e);
}
console.error(e.message);
}
如果不需要异常值,可以省略它及其周围的括号。
function isValidJSON(text) {
try {
JSON.parse(text);
return true;
} catch {
return false;
}
}
finally 块
finally
块包含在 try
块和 catch
块(如果有)执行之后但在 try...catch...finally
块后面的语句执行之前执行的语句。控制流将始终进入 finally
块,它可以以以下方式继续
- 在
try
块正常完成执行(并且没有抛出异常)之后立即; - 在
catch
块正常完成执行之后立即; - 在
try
块或catch
块中会导致退出块的控制流语句(return
、throw
、break
、continue
)执行之前立即。
如果 try
块中抛出异常,即使没有 catch
块来处理异常,finally
块仍然执行,在这种情况下,异常将在 finally
块完成执行后立即抛出。
以下示例显示了 finally
块的一个用例。代码打开一个文件,然后执行使用该文件的语句;finally
块确保即使抛出异常,文件也会在使用后始终关闭。
openMyFile();
try {
// tie up a resource
writeMyFile(theData);
} finally {
closeMyFile(); // always close the resource
}
finally
块中的控制流语句(return
、throw
、break
、continue
)将“屏蔽”try
块或 catch
块的任何完成值。在此示例中,try
块尝试返回 1,但在返回之前,控制流首先被让位于 finally
块,因此返回 finally
块的返回值。
function doIt() {
try {
return 1;
} finally {
return 2;
}
}
doIt(); // returns 2
通常在 finally
块中使用控制流语句不是一个好主意。仅将其用于清理代码。
示例
无条件 catch 块
当使用 catch
块时,当 try
块中抛出任何异常时,将执行 catch
块。例如,当以下代码中发生异常时,控制流将转移到 catch
块。
try {
throw "myException"; // generates an exception
} catch (e) {
// statements to handle any exceptions
logMyErrors(e); // pass exception object to error handler
}
catch
块指定一个标识符(上面的示例中的 e
),它保存异常的值;此值仅在 catch
块的 作用域 中可用。
条件 catch 块
可以通过将 try...catch
块与 if...else if...else
结构组合来创建“条件 catch
块”,如下所示
try {
myroutine(); // may throw three types of exceptions
} catch (e) {
if (e instanceof TypeError) {
// statements to handle TypeError exceptions
} else if (e instanceof RangeError) {
// statements to handle RangeError exceptions
} else if (e instanceof EvalError) {
// statements to handle EvalError exceptions
} else {
// statements to handle any unspecified exceptions
logMyErrors(e); // pass exception object to error handler
}
}
此操作的常见用例是仅捕获(并静默)一小部分预期错误,然后在其他情况下重新抛出错误
try {
myRoutine();
} catch (e) {
if (e instanceof RangeError) {
// statements to handle this very common expected error
} else {
throw e; // re-throw the error unchanged
}
}
这可能模拟其他语言(如 Java)中的语法
try {
myRoutine();
} catch (RangeError e) {
// statements to handle this very common expected error
}
// Other errors are implicitly re-thrown
嵌套 try 块
首先,让我们看看以下代码会发生什么
try {
try {
throw new Error("oops");
} finally {
console.log("finally");
}
} catch (ex) {
console.error("outer", ex.message);
}
// Logs:
// "finally"
// "outer" "oops"
现在,如果我们在内部的 `try` 代码块中通过添加 `catch` 代码块来捕获异常
try {
try {
throw new Error("oops");
} catch (ex) {
console.error("inner", ex.message);
} finally {
console.log("finally");
}
} catch (ex) {
console.error("outer", ex.message);
}
// Logs:
// "inner" "oops"
// "finally"
现在,让我们重新抛出错误。
try {
try {
throw new Error("oops");
} catch (ex) {
console.error("inner", ex.message);
throw ex;
} finally {
console.log("finally");
}
} catch (ex) {
console.error("outer", ex.message);
}
// Logs:
// "inner" "oops"
// "finally"
// "outer" "oops"
任何给定的异常只会由最近的封闭 `catch` 代码块捕获一次,除非它被重新抛出。当然,在“内部”代码块中引发的任何新异常(因为 `catch` 代码块中的代码可能会执行抛出异常的操作)将被“外部”代码块捕获。
从 finally 代码块中返回
如果 `finally` 代码块返回一个值,则该值将成为整个 `try-catch-finally` 语句的返回值,无论 `try` 和 `catch` 代码块中的任何 `return` 语句如何。这包括 `catch` 代码块内部抛出的异常。
(() => {
try {
try {
throw new Error("oops");
} catch (ex) {
console.error("inner", ex.message);
throw ex;
} finally {
console.log("finally");
return;
}
} catch (ex) {
console.error("outer", ex.message);
}
})();
// Logs:
// "inner" "oops"
// "finally"
外部的 “oops” 不会被抛出,因为 `finally` 代码块中存在返回语句。同样的事情也会发生在 `catch` 代码块中返回的任何值上。
规范
规范 |
---|
ECMAScript 语言规范 # sec-try-statement |
浏览器兼容性
BCD 表格仅在浏览器中加载