try ... catch 语句由一个 try 块和一个 catch 块或 finally 块(或两者皆有)组成。首先执行 try 块中的代码,如果它抛出异常,则将执行 catch 块中的代码。finally 块中的代码将在控制流退出整个结构之前始终被执行。
try {
tryStatements
} catch (exceptionVar) {
catchStatements
} finally {
finallyStatements
}
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);
控制流语句(return、throw、break、continue)在 finally 块中将“覆盖” try 块或 catch 块的任何正常完成值。在此示例中,try 块尝试返回 1,但在返回之前,控制流已转移到 finally 块,因此 finally 块的返回值将被返回
function doIt() {
try {
return 1;
} finally {
return 2;
}
}
doIt(); // 返回 2
在 finally 块中使用控制流语句通常不是一个好主意。请只将其用于清理代码
嵌套 try 块
首先,让我们看看下面的代码会发生什么
try {
try {
throw new Error("哦豁");
} finally {
console.log("finally");
}
} catch (ex) {
console.error("外层", ex.message);
}
// 输出:
// “finally”
// “外层” “哦豁”
现在,如果我们已经在内部的 try 块中通过添加 catch 块捕获了异常
try {
try {
throw new Error("哦豁");
} catch (ex) {
console.error("内层", ex.message);
} finally {
console.log("最终");
}
} catch (ex) {
console.error("外层", ex.message);
}
// 输出:
// “内层” “哦豁”
// “最终”
现在,让我们重新抛出错误
try {
try {
throw new Error("哦豁");
} catch (ex) {
console.error("内层", ex.message);
throw ex;
} finally {
console.log("最终");
}
} catch (ex) {
console.error("外层", ex.message);
}
// 输出:
// “内层” “哦豁”
// “最终”
// “外层” “哦豁”
任何特定的异常只会被直接包裹它的 catch 块捕获一次,除非该异常被重新抛出。当然,如果在“内部”代码块中触发了任何新的异常(因为 catch 块中的代码可能会执行某些操作并抛出异常),这些异常将由外部的 catch 块捕获
从 finally 块返回
如果 finally 块返回一个值,这个值将成为整个 try-catch-finally 语句的返回值,而不管 try 和 catch 块中的 return 语句。这包括 catch 块中抛出的异常
(() => {
try {
try {
throw new Error("哦豁");
} catch (ex) {
console.error("内层", ex.message);
throw ex;
} finally {
console.log("最终");
return;
}
} catch (ex) {
console.error("外层", ex.message);
}
})();
// 输出:
// “内层” “哦豁”
// “最终”
外层的“哦豁”不会被抛出,因为 finally 块中的 return 语句将控制流转移到外部。同样的规则也适用于 catch 块中返回的值