try...catch

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

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

试一试

try {
  nonExistentFunction();
} catch (error) {
  console.error(error);
  // Expected output: ReferenceError: nonExistentFunction is not defined
  // (Note: the exact output may be browser-dependent)
}

语法

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 new Error("My exception"); // 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"

现在,如果我们通过添加一个 catch 块已经在内部 try 块中捕获了异常

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 语句的返回值,而不管 trycatch 块中的任何 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® 2026 语言规范
# sec-try-statement

浏览器兼容性

另见