Back

理解 JavaScript 错误类型和消息

理解 JavaScript 错误类型和消息

每个 JavaScript 开发者都曾盯着控制台中的红色错误消息,思考到底哪里出了问题。理解这些错误类型不仅仅是为了修复损坏的代码——更是为了编写更可靠的应用程序并更快地调试问题。当你了解每种错误的含义及其发生原因时,你就能从被动调试转变为主动预防错误。

核心要点

  • JavaScript 错误是结构化对象,具有 namemessage 属性,可提供有关代码问题的具体反馈
  • 存在八种原生错误类型:SyntaxError、ReferenceError、TypeError、RangeError、URIError、EvalError、AggregateError 和 InternalError
  • 使用 try-catch 块和自定义错误类进行适当的异常处理可以提高应用程序的可靠性
  • 全局错误处理器和浏览器调试工具对于生产环境的错误跟踪至关重要

什么是 JavaScript 错误?

JavaScript 错误是表示代码执行过程中出现问题的对象。每个错误都包含一个标识其类型的 name 属性和一个描述问题的 message 属性。这些不是随机故障——它们是关于代码中特定问题的结构化反馈。

八种原生错误类型

SyntaxError:违反语法规则

SyntaxError(语法错误)发生在 JavaScript 由于无效语法而无法解析你的代码时。解释器甚至在执行开始之前就会停止。

// Missing closing parenthesis
console.log("Hello World");
// SyntaxError: missing ) after argument list

// Invalid JSON parsing
JSON.parse("{'invalid': 'json'}");
// SyntaxError: Unexpected token ' in JSON at position 1

常见原因包括缺少括号、未闭合的字符串或格式错误的 JSON。这些错误会完全阻止代码运行。

ReferenceError:使用未定义的内容

ReferenceError(引用错误)发生在你尝试访问当前作用域中不存在的变量时。

console.log(userName);
// ReferenceError: userName is not defined

function greet() {
  let message = "Hello";
}
greet();
console.log(message);
// ReferenceError: message is not defined

这通常是由拼写错误、在声明前访问变量或作用域混淆导致的。

TypeError:错误类型操作

TypeError(类型错误)发生在你对不兼容的类型执行操作时——比如调用非函数或访问 nullundefined 的属性。

const num = 42;
num();
// TypeError: num is not a function

const user = null;
console.log(user.name);
// TypeError: Cannot read properties of null (reading 'name')

这些是 JavaScript 应用程序中最常见的运行时错误之一。

RangeError:超出范围

RangeError(范围错误)表示某个值超出了其允许的范围。

const arr = new Array(-1);
// RangeError: Invalid array length

const num = 1;
num.toPrecision(101);
// RangeError: toPrecision() argument must be between 1 and 100

在使用数组构造函数、数字方法或超出调用栈的递归函数时要注意这些错误。

URIError:格式错误的 URI

URIError(URI 错误)出现在 URI 编码或解码函数接收到无效参数时。

decodeURIComponent('%');
// URIError: URI malformed

encodeURI('\uD800');
// URIError: URI malformed

这些错误在处理带有特殊字符或不完整百分比编码的 URL 时会出现。

EvalError:eval() 问题

虽然 EvalError 存在于规范中,但现代 JavaScript 不会抛出它。它保留下来是为了向后兼容。历史上与 eval() 相关的问题会触发此错误类型。

AggregateError:多个失败

AggregateError(聚合错误)将多个错误包装到一个对象中,在使用 Promise.any() 时特别有用。

Promise.any([
  Promise.reject(new Error("First failure")),
  Promise.reject(new Error("Second failure"))
]).catch(err => {
  console.log(err instanceof AggregateError); // true
  console.log(err.errors); // Array of individual errors
});

InternalError:引擎限制

InternalError(内部错误,非标准,Firefox 特有)发生在 JavaScript 引擎达到内部限制时。

function recursiveFunction() {
  recursiveFunction();
}
recursiveFunction();
// InternalError: too much recursion (Firefox)
// RangeError: Maximum call stack size exceeded (Chrome)

使用 try…catch 进行异常处理

JavaScript 通过 trycatchfinallythrow 语句提供结构化的异常处理

try {
  // Code that might throw an error
  const data = JSON.parse(userInput);
  processData(data);
} catch (error) {
  if (error instanceof SyntaxError) {
    console.error("Invalid JSON format:", error.message);
  } else if (error instanceof TypeError) {
    console.error("Data processing error:", error.message);
  } else {
    // Re-throw unknown errors
    throw error;
  }
} finally {
  // Cleanup code runs regardless
  closeConnections();
}

throw 语句创建自定义错误:

function validateAge(age) {
  if (age < 0) {
    throw new RangeError("Age cannot be negative");
  }
  if (typeof age !== 'number') {
    throw new TypeError("Age must be a number");
  }
  return age;
}

错误处理最佳实践

使用特定的错误类型来抛出自定义错误。不要使用通用的 Error 对象,而是针对类型问题抛出 TypeError,针对边界违规抛出 RangeError

在正确的层级处理错误。不要在每个函数中都包裹 try-catch。在你能够有意义地响应错误的地方处理它们。

在重新抛出时保留错误上下文:

try {
  riskyOperation();
} catch (error) {
  // Add context without losing original stack trace
  error.message = `Failed during user ${userId} operation: ${error.message}`;
  throw error;
}

为特定领域的问题创建自定义错误类:

class ValidationError extends Error {
  constructor(field, value) {
    super(`Invalid value for ${field}: ${value}`);
    this.name = 'ValidationError';
    this.field = field;
    this.value = value;
  }
}

有效调试 JavaScript 错误

现代浏览器提供了出色的调试工具。堆栈跟踪显示导致错误的执行路径。使用断点在错误发生前暂停执行。控制台的错误消息通常包含确切的行号和文件。

对于生产应用程序,实现全局错误处理器:

window.addEventListener('error', (event) => {
  console.error('Global error:', event.error);
  // Send to error tracking service
});

window.addEventListener('unhandledrejection', (event) => {
  console.error('Unhandled promise rejection:', event.reason);
});

结论

理解 JavaScript 的错误类型将调试从猜测工作转变为系统化的问题解决。每种错误类型——从 SyntaxErrorAggregateError——都会准确告诉你出了什么问题以及应该查看哪里。结合使用 try-catch 块的适当异常处理和有意义的错误消息,你可以构建优雅失败并在出现问题时提供清晰反馈的应用程序。关键不是避免所有错误——而是在错误发生时智能地处理它们。

常见问题

抛出错误会立即停止执行并向上冒泡,直到被 try-catch 块捕获或导致程序崩溃。返回错误值会保持执行流程,但需要显式检查。对于应该中断正常流程的异常情况使用 throw,对于预期的失败使用返回值。

使用带有 unhandledrejection 事件的 window.addEventListener 来全局捕获 Promise 拒绝。对于异步函数,将它们包装在 try-catch 块中或为返回的 Promise 附加 catch 处理器。这可以防止静默失败并帮助跟踪生产环境中的错误。

每个 JavaScript 引擎在遵循相同的 ECMAScript 规范定义错误类型的同时,会以不同方式实现错误消息。Chrome 的 V8、Firefox 的 SpiderMonkey 和 Safari 的 JavaScriptCore 可能会以独特的方式表述消息,但错误类型和行为保持一致。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay