Skip to content

try...catch

通常,如果发生错误,脚本就会“死亡”(立即停止),并在控制台将错误打印出来。

但是有一种语法结构 try...catch,它使我们可以“捕获(catch)”错误,因此脚本可以执行更合理的操作,而不是死掉。

语法:

javascript
try {
  // 代码...
} catch (err) {
  // 错误捕获
}

它按照以下步骤执行:

  1. 首先,执行 try {...} 中的代码。
  2. 如果这里没有错误,则忽略 catch (err):执行到 try 的末尾并跳过 catch 继续执行。
  3. 如果这里出现错误,则 try 执行停止,控制流转向 catch (err) 的开头。变量 err(我们可以使用任何名称)将包含一个error 对象,该对象包含了所发生事件的详细信息。 Pasted image 20250627170828.png
javascript
try {
    shit;
} catch (error) {
    console.log('error ' + error);//error ReferenceError: shit is not defined
}

try...catch 只能处理有效代码中出现的错误。这类错误被称为“运行时的错误(runtime errors)”,有时被称为“异常(exceptions)”。 它必须是有效的 JavaScript 代码。

如果代码包含语法错误,那么 try..catch 将无法正常工作,例如含有不匹配的花括号


如果在“计划的(scheduled)”代码中发生异常,例如在 setTimeout 中,则 try...catch 不会捕获到异常:

javascript
try {
    setTimeout(function () {
        try {
            noSuchVariable;
        } catch (error) {
            console.log('内部');//
        }
    }, 1000);
} catch (err) {
    console.log("不工作");//
}

引擎是同步执行的,所以在执行到setTimeout的时候已经离开了try,为了捕获这个错误,try必须在setTimeout的内部,包裹住可能发生错误的位置。


Error对象

对于所有内建的 error,error 对象具有两个主要属性:

name Error 名称。例如,对于一个未定义的变量,名称是 "ReferenceError"

message 关于 error 的详细文字描述。

还有其他非标准的属性在大多数环境中可用。其中被最广泛使用和支持的是: stack 当前的调用栈:用于调试目的的一个字符串,其中包含有关导致 error 的嵌套调用序列的信息。

如果我们不需要 error 的详细信息,catch 也可以忽略它

使用throw会生成一个错误对象,并且抛出。在javascript中有很多的内建构造函数,ErrorSyntaxErrorReferenceErrorTypeError 等,用来构造一个错误对象。对于内建的 error(不是对于其他任何对象,仅仅是对于 error),name 属性刚好就是构造器的名字。message 则来自于参数(argument)。我们也可以自己来写:

javascript
let json = '{ "age": 30 }'; // 不完整的数据

try {

    let user = JSON.parse(json); // <-- 没有 error

    if (!user.name) {
        throw new SyntaxError("数据不全:没有 name"); // (*)
    }

    alert(user.name);

} catch (err) {
    alert("JSON Error: " + err.message); // JSON Error: 数据不全:没有 name
}

再次抛出:我们可以使用 instanceof 操作符判断错误类型

  1. Catch 捕获所有 error。
  2. 在 catch (err) {...} 块中,我们对 error 对象 err 进行分析。
  3. 如果我们不知道如何处理它,那我们就 throw err
javascript
function readData() {
  let json = '{ "age": 30 }';

  try {
    // ...
    blabla(); // error!
  } catch (err) {
    // ...
    if (!(err instanceof SyntaxError)) {
      throw err; // 再次抛出(不知道如何处理它)
    }
  }
}

try {
  readData();
} catch (err) {
  alert( "External catch got: " + err ); // 捕获了它!
}

抛出的错误再次进行捕捉,使用外层的catch来处理

try...catch 结构可能还有一个代码子句(clause):finally。 如果它存在,它在所有情况下都会被执行:

  • try 之后,如果没有 error,
  • catch 之后,如果有 error。
javascript
try {
   ... 尝试执行的代码 ...
} catch (err) {
   ... 处理 error ...
} finally {
   ... 总是会执行的代码 ...
}

finally 子句适用于 try...catch 的 任何 出口。这包括显式的 return

在下面这个例子中,在 try 中有一个 return。在这种情况下,finally 会在控制转向外部代码前被执行 :

javascript
function func() {

  try {
    return 1;

  } catch (err) {
    /* ... */
  } finally {
    alert( 'finally' );
  }
}

alert( func() ); // 先执行 finally 中的 alert,然后执行这个 alert

没有 catch 子句的 try...finally 结构也很有用。当我们不想在原地处理 error(让它们掉出去吧),但是需要确保我们启动的处理需要被完成时,我们应当使用它

javascript
function func() {
  // 开始执行需要被完成的操作(比如测量)
  try {
    // ...
  } finally {
    // 完成前面我们需要完成的那件事,即使 try 中的执行失败了
  }
}

自定义Error,Error的继承

javascript
class ValidationError extends Error {
  constructor(message) {
    super(message); // (1)
    this.name = "ValidationError"; // (2)
  }
}

try {
  let user = readUser('{ "age": 25 }');
} catch (err) {
  if (err instanceof ValidationError) {
    alert("Invalid data: " + err.message); // Invalid data: No field: name
  } else if (err instanceof SyntaxError) { // (*)
    alert("JSON Syntax Error: " + err.message);
  } else {
    throw err; // 未知的 error,再次抛出 (**)
  }
}

还有,没有学完。