Skip to content

ES6 - 错误处理

错误处理是编写健壮的 JavaScript 应用程序的关键方面。它涉及预测、检测和响应程序执行期间可能发生的错误。

  • 语法错误 (Syntax Errors): 当代码违反 JavaScript 的语法规则时发生。这些错误由 JavaScript 引擎在解析阶段(执行之前)检测到,并阻止脚本(或模块)运行。示例:缺少括号。
  • 运行时错误 (Runtime Errors),也称为异常 (Exceptions): 在代码执行期间发生。当引擎遇到无法执行的操作时会发生这些错误。示例:试图在一个 undefined 变量上调用方法,除以零(尽管 JS 返回 Infinity),或在严格模式下引用一个未声明的变量。
  • 逻辑错误 (Logical Errors): 代码运行没有抛出语法错误或运行时错误,但由于程序逻辑中的缺陷,未能产生预期结果。这些错误通常是最难调试的,因为引擎不会将其报告为错误。

这是在同步 JavaScript 代码中处理运行时错误(异常)的主要机制。

  • try 块: 包含可能抛出异常的代码。
  • catch (error) 块: 如果在 try 块中发生异常,控制权将转移到 catch 块。error 对象(或您给参数取的任何名称)包含有关错误的信息。
  • finally 块 (可选): 包含将在 try 和 catch 块之后执行的代码,无论是否抛出或捕获了异常。它通常用于清理操作(例如,关闭资源)。
try {
// 可能抛出错误的代码
} catch (error) {
// 处理错误的代码
// 'error' 是一个 Error 对象
} finally {
// 总是会执行的代码
}
function divide(a, b) {
try {
if (b === 0) {
throw new Error("Division by zero is not allowed.");
}
const result = a / b;
console.log(`Result: ${result}`);
return result;
} catch (error) {
console.error(`Error Name: ${error.name}`);
console.error(`Error Message: ${error.message}`);
// console.error(`Stack Trace: ${error.stack}`); // 对调试很有用
return null; // 或者以不同方式处理
} finally {
console.log("Division attempt finished.");
}
}
divide(10, 2); // Output: Result: 5, Division attempt finished.
divide(10, 0); // Output: Error messages, Division attempt finished.

当发生运行时错误时,JavaScript 会创建一个并抛出一个 Error 对象(或其某个子类型的实例)。Error 对象的主要属性包括:

  • name: 错误的类型(例如,"Error","TypeError","ReferenceError")。
  • message: 错误的人类可读描述。
  • stack (非标准但广泛支持): 一个包含调用栈跟踪信息的字符串,对调试很有用。
序号错误类型描述
1Error通用的基本错误对象。
2EvalError表示与全局 eval() 函数相关的错误。在现代 JavaScript 中很少抛出;其他错误更常见。
3RangeError表示一个数字变量或参数超出了其有效范围。(例如,new Array(-1))
4ReferenceError尝试访问一个尚未声明的变量(在严格模式下)或超出作用域的变量时发生。
5SyntaxError表示代码中的解析错误。这些错误通常会阻止执行,并且不能被 try...catch 捕获,除非使用了 eval() 或类似方式。
6TypeError执行某个操作时发生,通常是当值不是预期类型时(例如,在 null 或 undefined 上调用方法,或将非函数视为函数)。
7URIError表示 encodeURI()、decodeURI()、encodeURIComponent() 或 decodeURIComponent() 被传入了无效参数。

您可以使用 throw 语句创建并抛出自己的异常。您可以抛出任何表达式,但最佳实践是抛出一个 Error 对象或从 Error 派生的自定义错误类的实例。

语法: throw new ErrorType("描述性信息");

function validateAge(age) {
if (typeof age !== 'number') {
throw new TypeError("Age must be a number.");
}
if (age < 0) {
throw new RangeError("Age cannot be negative.");
}
console.log("Age is valid.");
}
try {
validateAge(-5);
} catch (e) {
console.error(`${e.name}: ${e.message}`); // Output: RangeError: Age cannot be negative.
}

为了进行更具体的错误处理,您可以通过扩展内置的 Error 类来创建自定义错误类。

class NetworkError extends Error {
constructor(message, statusCode) {
super(message); // 调用父类构造函数 (Error)
this.name = "NetworkError";
this.statusCode = statusCode;
}
}
try {
// 模拟一个网络问题
throw new NetworkError("Failed to connect to the server.", 503);
} catch (e) {
if (e instanceof NetworkError) {
console.error(`Custom Error (${e.name}): ${e.message}, Status: ${e.statusCode}`);
} else {
console.error(`Generic Error: ${e.message}`);
}
}

try...catch 只适用于同步代码。对于涉及 Promise 或 setTimeout 等异步操作:

  • Promise: 使用 .catch(errorHandler) 方法或 .then(successHandler, errorHandler) 中的第二个回调函数。
  • async/await: 您可以在 await 表达式周围使用 try...catch 来处理被等待的 Promise 拒绝 (rejections)。
  • 回调函数 (Callbacks): 错误处理通常遵循错误优先的回调约定(例如,callback(error, data))。
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Failed to fetch data:", error);
// 处理或重新抛出
}
}
fetchData();

window.onerror 和 window.onunhandledrejection

Section titled “window.onerror 和 window.onunhandledrejection”
  • window.onerror = function(message, source, lineno, colno, error): 这是由窗口加载的脚本中发生的未捕获同步错误的全局事件处理程序。它是用于错误日志记录或报告的最后手段。

  • window.onunhandledrejection = function(event): 这是未捕获 Promise 拒绝的全局事件处理程序。event.reason 属性包含拒绝的原因值。

这些全局处理程序对于遥测和全局错误跟踪很有用,但不应取代用于应用程序逻辑的特定 try...catch 块或 Promise 的 .catch() 处理程序。

了解更多关于错误处理的信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Control_flow_and_error_handling