try...catch

try...catch 语句由一个 try 块和一个 catch 块、一个 finally 块或两者组成。try 块中的代码首先执行,如果它抛出异常,catch 块中的代码将被执行。finally 块中的代码将在控制流退出整个构造之前始终执行。

尝试

语法

js
try {
  tryStatements
} catch (exceptionVar) {
  catchStatements
} finally {
  finallyStatements
}
tryStatements

要执行的语句。

catchStatements

如果 try 块中抛出异常,则执行的语句。

exceptionVar 可选

一个可选的 标识符或模式,用于保存与关联 catch 块捕获的异常。如果 catch 块不使用异常的值,可以省略 exceptionVar 及其周围的括号。

finallyStatements

在控制流退出 try...catch...finally 构造之前执行的语句。这些语句无论是否抛出或捕获异常都会执行。

描述

try 语句始终以一个 try 块开头。然后,必须存在一个 catch 块或一个 finally 块。也可以同时具有 catchfinally 块。这给了我们 try 语句的三种形式

  • try...catch
  • try...finally
  • try...catch...finally

与其他构造(如 iffor)不同,trycatchfinally 块必须是,而不是单个语句。

js
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 块的 作用域 中可用。

它不需要是单个标识符。可以使用 解构模式 来一次分配多个标识符。

js
try {
  throw new TypeError("oops");
} catch ({ name, message }) {
  console.log(name); // "TypeError"
  console.log(message); // "oops"
}

catch 子句创建的绑定与 catch 块位于相同的范围,因此在 catch 块中声明的任何变量不能与由 catch 子句创建的绑定具有相同的名称。(对此规则有一个 例外,但它是已弃用的语法。)

js
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 对象。

js
try {
  throw "Oops; this is not an Error object";
} catch (e) {
  if (!(e instanceof Error)) {
    e = new Error(e);
  }
  console.error(e.message);
}

如果不需要异常值,可以省略它及其周围的括号。

js
function isValidJSON(text) {
  try {
    JSON.parse(text);
    return true;
  } catch {
    return false;
  }
}

finally 块

finally 块包含在 try 块和 catch 块(如果有)执行之后但在 try...catch...finally 块后面的语句执行之前执行的语句。控制流将始终进入 finally 块,它可以以以下方式继续

  • try 块正常完成执行(并且没有抛出异常)之后立即;
  • catch 块正常完成执行之后立即;
  • try 块或 catch 块中会导致退出块的控制流语句(returnthrowbreakcontinue)执行之前立即。

如果 try 块中抛出异常,即使没有 catch 块来处理异常,finally 块仍然执行,在这种情况下,异常将在 finally 块完成执行后立即抛出。

以下示例显示了 finally 块的一个用例。代码打开一个文件,然后执行使用该文件的语句;finally 块确保即使抛出异常,文件也会在使用后始终关闭。

js
openMyFile();
try {
  // tie up a resource
  writeMyFile(theData);
} finally {
  closeMyFile(); // always close the resource
}

finally 块中的控制流语句(returnthrowbreakcontinue)将“屏蔽”try 块或 catch 块的任何完成值。在此示例中,try 块尝试返回 1,但在返回之前,控制流首先被让位于 finally 块,因此返回 finally 块的返回值。

js
function doIt() {
  try {
    return 1;
  } finally {
    return 2;
  }
}

doIt(); // returns 2

通常在 finally 块中使用控制流语句不是一个好主意。仅将其用于清理代码。

示例

无条件 catch 块

当使用 catch 块时,当 try 块中抛出任何异常时,将执行 catch 块。例如,当以下代码中发生异常时,控制流将转移到 catch 块。

js
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 块”,如下所示

js
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
  }
}

此操作的常见用例是仅捕获(并静默)一小部分预期错误,然后在其他情况下重新抛出错误

js
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)中的语法

java
try {
  myRoutine();
} catch (RangeError e) {
  // statements to handle this very common expected error
}
// Other errors are implicitly re-thrown

嵌套 try 块

首先,让我们看看以下代码会发生什么

js
try {
  try {
    throw new Error("oops");
  } finally {
    console.log("finally");
  }
} catch (ex) {
  console.error("outer", ex.message);
}

// Logs:
// "finally"
// "outer" "oops"

现在,如果我们在内部的 `try` 代码块中通过添加 `catch` 代码块来捕获异常

js
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"

现在,让我们重新抛出错误。

js
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` 代码块内部抛出的异常。

js
(() => {
  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 表格仅在浏览器中加载

另请参阅