Skip to content

错误类型:理解 JavaScript 的内置错误

在医学中,医生需要准确诊断疾病类型——是感冒、肺炎还是过敏——才能对症下药。同样,在编程中,理解不同类型的错误能帮助你快速定位问题、采取正确的修复策略。JavaScript 提供了多种内置错误类型,每种都代表特定类别的问题。

Error:所有错误的基类

Error 是所有内置错误类型的父类。它本身可以直接使用,也可以作为自定义错误的基类:

javascript
let error = new Error("Something went wrong");

console.log(error.name); // "Error"
console.log(error.message); // "Something went wrong"
console.log(error.stack); // 调用栈信息

所有错误对象都具有这三个核心属性:

  • name:错误类型名称
  • message:错误描述信息
  • stack:错误发生时的调用栈(用于调试)

虽然可以直接使用 Error,但更具体的错误类型能提供更多信息。让我们逐一了解它们。

ReferenceError:引用错误

当你尝试访问一个不存在的变量时,会抛出 ReferenceError

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

这是最常见的错误之一,通常由以下原因引起:

1. 变量拼写错误

javascript
let userName = "Alice";
console.log(usrName); // ReferenceError: usrName is not defined

2. 变量作用域问题

javascript
function test() {
  let localVar = "I'm local";
}

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

3. 在变量声明前访问(对于 let 和 const)

javascript
console.log(myVar); // ReferenceError: Cannot access 'myVar' before initialization
let myVar = 10;

这被称为"暂时性死区"(Temporal Dead Zone, TDZ)。letconst 声明的变量在声明前不可访问,而 var 会提升(hoisting)并返回 undefined

javascript
console.log(myVar); // undefined(不是 ReferenceError)
var myVar = 10;

处理 ReferenceError

javascript
try {
  console.log(possiblyUndefined);
} catch (error) {
  if (error instanceof ReferenceError) {
    console.log("Variable does not exist");
    // 使用默认值或采取其他行动
  }
}

TypeError:类型错误

TypeError 在你对某个值执行不兼容的操作时抛出,比如把非函数当函数调用,或访问 null/undefined 的属性:

1. 调用非函数

javascript
let value = 42;
value(); // TypeError: value is not a function

let obj = { name: "Alice" };
obj.method(); // TypeError: obj.method is not a function

2. 访问 null 或 undefined 的属性

javascript
let user = null;
console.log(user.name); // TypeError: Cannot read property 'name' of null

let data = undefined;
console.log(data.length); // TypeError: Cannot read property 'length' of undefined

这是非常常见的错误,通常需要先检查值是否存在:

javascript
let user = null;

// ✅ 安全访问
if (user) {
  console.log(user.name);
} else {
  console.log("User is null");
}

// ✅ 使用可选链(ES2020+)
console.log(user?.name); // undefined(不抛出错误)

3. 对不可变值赋值

javascript
const PI = 3.14;
PI = 3.14159; // TypeError: Assignment to constant variable

const obj = { name: "Alice" };
obj = { name: "Bob" }; // TypeError: Assignment to constant variable

// 但可以修改对象的属性
obj.name = "Bob"; // ✅ 这是允许的

4. 调用不存在的方法

javascript
let num = 123;
num.toUpperCase(); // TypeError: num.toUpperCase is not a function

处理 TypeError

javascript
function getUserName(user) {
  try {
    return user.name.toUpperCase();
  } catch (error) {
    if (error instanceof TypeError) {
      console.log("Invalid user object");
      return "Unknown";
    }
    throw error;
  }
}

console.log(getUserName(null)); // Invalid user object, 返回 "Unknown"
console.log(getUserName({ name: "Alice" })); // "ALICE"

更好的做法是提前检查:

javascript
function getUserName(user) {
  if (!user || !user.name) {
    return "Unknown";
  }

  if (typeof user.name !== "string") {
    return "Unknown";
  }

  return user.name.toUpperCase();
}

SyntaxError:语法错误

SyntaxError 表示代码的语法有问题,JavaScript 引擎无法解析:

javascript
// 缺少右括号
function test( {
  console.log("hello");
}

// 非法的对象字面量
let obj = { a: 1, b: };

// 非法的 JSON
JSON.parse('{ invalid json }'); // SyntaxError: Unexpected token i in JSON

大多数 SyntaxError 会在代码加载时就被检测到,而不是运行时。但动态执行的代码(如 evalJSON.parse)可能在运行时抛出:

javascript
try {
  eval("let x = ;"); // SyntaxError: Unexpected token ';'
} catch (error) {
  if (error instanceof SyntaxError) {
    console.log("Invalid syntax");
  }
}

try {
  JSON.parse('{ "name": "Alice" }'); // ✅ 有效的 JSON
  JSON.parse("{ name: 'Alice' }"); // SyntaxError: 无效的 JSON(键必须用双引号)
} catch (error) {
  if (error instanceof SyntaxError) {
    console.log("Invalid JSON format");
  }
}

常见的 SyntaxError 场景

javascript
// ❌ 意外的标识符
let name = John; // SyntaxError: John is not defined(应该是字符串)
let name = "John"; // ✅

// ❌ 意外的 token
let arr = [1, 2, 3,]; // 在旧浏览器中可能报错(尾随逗号)
let arr = [1, 2, 3];  // ✅

// ❌ 缺少分号或逗号
let obj = {
  name: "Alice"
  age: 30  // SyntaxError: 缺少逗号
};

RangeError:范围错误

RangeError 在数值超出有效范围时抛出:

1. 无效的数组长度

javascript
let arr = new Array(-1); // RangeError: Invalid array length
let arr2 = new Array(4294967296); // RangeError: Invalid array length(太大)

数组长度必须是非负整数,且小于 2^32。

2. 递归调用过深(栈溢出)

javascript
function recursiveFunction() {
  recursiveFunction(); // 无限递归
}

recursiveFunction(); // RangeError: Maximum call stack size exceeded

3. 数值方法的无效参数

javascript
let num = 123.456;
num.toFixed(-1); // RangeError: toFixed参数必须在 0 到 100 之间
num.toFixed(101); // RangeError

num.toPrecision(0); // RangeError: toPrecision 参数必须在 1 到 100 之间

处理 RangeError

javascript
function safeToFixed(num, digits) {
  try {
    if (digits < 0 || digits > 100) {
      digits = 2; // 使用默认值
    }
    return num.toFixed(digits);
  } catch (error) {
    if (error instanceof RangeError) {
      console.log("Invalid precision, using default");
      return num.toFixed(2);
    }
    throw error;
  }
}

console.log(safeToFixed(123.456, -1)); // Invalid precision, 返回 "123.46"
console.log(safeToFixed(123.456, 3)); // "123.456"

URIError:URI 处理错误

URIError 在使用全局 URI 处理函数时传入无效参数会抛出:

javascript
try {
  decodeURIComponent("%"); // URIError: URI malformed
} catch (error) {
  if (error instanceof URIError) {
    console.log("Invalid URI component");
  }
}

// ✅ 有效的 URI 编码
let encoded = encodeURIComponent("Hello World"); // "Hello%20World"
let decoded = decodeURIComponent(encoded); // "Hello World"

// ❌ 无效的 URI
decodeURIComponent("%E0%A4%A"); // URIError: URI malformed

这个错误相对少见,主要在处理 URL 编码/解码时可能遇到。

EvalError:Eval 错误

EvalError 理论上与 eval() 函数有关,但在现代 JavaScript 中几乎不再使用。当前的规范中,eval() 的错误通常会抛出其他类型的错误(如 SyntaxError)。

javascript
// 现代 JavaScript 不抛出 EvalError
eval("let x = 1"); // ✅ 正常工作

// 语法错误会抛出 SyntaxError
try {
  eval("let x = ;");
} catch (error) {
  console.log(error.name); // SyntaxError(不是 EvalError)
}

因此,你几乎不需要处理 EvalError

InternalError:内部错误(非标准)

InternalError 是一些浏览器(如 Firefox)的非标准扩展,表示 JavaScript 引擎内部错误:

javascript
// 可能导致 InternalError 的情况(仅 Firefox)
// - 过多的 switch case
// - 正则表达式过于复杂
// - 过大的数组初始化器

由于这不是标准错误,不应该在跨浏览器的代码中依赖它。

AggregateError:聚合错误(ES2021)

AggregateError 用于表示多个错误的集合,常见于 Promise.any() 失败时:

javascript
Promise.any([
  Promise.reject(new Error("Error 1")),
  Promise.reject(new Error("Error 2")),
  Promise.reject(new Error("Error 3")),
]).catch((error) => {
  console.log(error instanceof AggregateError); // true
  console.log(error.errors); // [Error: Error 1, Error: Error 2, Error: Error 3]
});

也可以手动创建:

javascript
let errors = [
  new Error("Failed to load user"),
  new Error("Failed to load posts"),
  new Error("Failed to load comments"),
];

throw new AggregateError(errors, "Multiple operations failed");

错误类型的实际应用

根据错误类型采取不同策略

javascript
function handleError(error) {
  if (error instanceof TypeError) {
    console.log("Type error: check your data types");
    // 可能是数据格式问题,尝试使用默认值
  } else if (error instanceof ReferenceError) {
    console.log("Reference error: variable does not exist");
    // 可能是拼写错误或作用域问题
  } else if (error instanceof RangeError) {
    console.log("Range error: value out of range");
    // 可能是参数验证问题
  } else if (error instanceof SyntaxError) {
    console.log("Syntax error: invalid code or data format");
    // 可能是 JSON 解析失败
  } else {
    console.log("Unknown error:", error.message);
  }
}

try {
  JSON.parse("{ invalid }");
} catch (error) {
  handleError(error); // Syntax error: invalid code or data format
}

区分可恢复和不可恢复错误

javascript
async function loadData() {
  try {
    let response = await fetch("/api/data");
    let data = await response.json();
    return processData(data);
  } catch (error) {
    if (error instanceof SyntaxError) {
      // JSON 解析失败:可恢复,使用默认数据
      console.log("Invalid data format, using defaults");
      return getDefaultData();
    } else if (error instanceof TypeError) {
      // 类型错误:可能是数据结构问题,尝试修复
      console.log("Data structure issue, attempting repair");
      return repairAndProcess(error);
    } else {
      // 其他错误:不可恢复,向上抛出
      throw error;
    }
  }
}

详细的错误日志

javascript
function logError(error) {
  let errorInfo = {
    type: error.name,
    message: error.message,
    stack: error.stack,
    timestamp: new Date().toISOString(),
  };

  // 根据错误类型添加额外信息
  if (error instanceof RangeError) {
    errorInfo.category = "range-violation";
  } else if (error instanceof TypeError) {
    errorInfo.category = "type-mismatch";
  } else if (error instanceof ReferenceError) {
    errorInfo.category = "undefined-reference";
  }

  console.error("Error occurred:", errorInfo);
  // 发送到日志服务
  // sendToLoggingService(errorInfo);
}

检查错误类型的最佳实践

使用 instanceof

javascript
try {
  someRiskyOperation();
} catch (error) {
  if (error instanceof TypeError) {
    // 处理类型错误
  } else if (error instanceof ReferenceError) {
    // 处理引用错误
  }
}

检查 error.name

javascript
try {
  someRiskyOperation();
} catch (error) {
  switch (error.name) {
    case "TypeError":
      console.log("Type error occurred");
      break;
    case "ReferenceError":
      console.log("Reference error occurred");
      break;
    default:
      console.log("Unknown error:", error.name);
  }
}

不要依赖 error.message

javascript
// ❌ 不可靠:错误消息可能改变或不同
if (error.message.includes("not defined")) {
  // ...
}

// ✅ 使用错误类型
if (error instanceof ReferenceError) {
  // ...
}

总结

理解 JavaScript 的内置错误类型能帮助你快速诊断问题、采取正确的处理策略。每种错误类型都代表特定类别的问题,识别它们能让你的错误处理更精确、更有效。

关键要点:

  • Error:所有错误的基类
  • ReferenceError:访问不存在的变量
  • TypeError:类型不匹配或对值进行不兼容的操作
  • SyntaxError:代码语法错误
  • RangeError:数值超出有效范围
  • URIError:URI 处理函数的无效参数
  • AggregateError:多个错误的集合
  • 使用 instanceof 检查错误类型
  • 根据错误类型采取不同的处理策略
  • 不要依赖 error.message 进行判断

掌握这些错误类型,能让你在调试时更快定位问题,在编码时写出更健壮的错误处理逻辑。