错误类型:理解 JavaScript 的内置错误
在医学中,医生需要准确诊断疾病类型——是感冒、肺炎还是过敏——才能对症下药。同样,在编程中,理解不同类型的错误能帮助你快速定位问题、采取正确的修复策略。JavaScript 提供了多种内置错误类型,每种都代表特定类别的问题。
Error:所有错误的基类
Error 是所有内置错误类型的父类。它本身可以直接使用,也可以作为自定义错误的基类:
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:
console.log(nonExistentVariable); // ReferenceError: nonExistentVariable is not defined这是最常见的错误之一,通常由以下原因引起:
1. 变量拼写错误
let userName = "Alice";
console.log(usrName); // ReferenceError: usrName is not defined2. 变量作用域问题
function test() {
let localVar = "I'm local";
}
test();
console.log(localVar); // ReferenceError: localVar is not defined3. 在变量声明前访问(对于 let 和 const)
console.log(myVar); // ReferenceError: Cannot access 'myVar' before initialization
let myVar = 10;这被称为"暂时性死区"(Temporal Dead Zone, TDZ)。let 和 const 声明的变量在声明前不可访问,而 var 会提升(hoisting)并返回 undefined:
console.log(myVar); // undefined(不是 ReferenceError)
var myVar = 10;处理 ReferenceError
try {
console.log(possiblyUndefined);
} catch (error) {
if (error instanceof ReferenceError) {
console.log("Variable does not exist");
// 使用默认值或采取其他行动
}
}TypeError:类型错误
TypeError 在你对某个值执行不兼容的操作时抛出,比如把非函数当函数调用,或访问 null/undefined 的属性:
1. 调用非函数
let value = 42;
value(); // TypeError: value is not a function
let obj = { name: "Alice" };
obj.method(); // TypeError: obj.method is not a function2. 访问 null 或 undefined 的属性
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这是非常常见的错误,通常需要先检查值是否存在:
let user = null;
// ✅ 安全访问
if (user) {
console.log(user.name);
} else {
console.log("User is null");
}
// ✅ 使用可选链(ES2020+)
console.log(user?.name); // undefined(不抛出错误)3. 对不可变值赋值
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. 调用不存在的方法
let num = 123;
num.toUpperCase(); // TypeError: num.toUpperCase is not a function处理 TypeError
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"更好的做法是提前检查:
function getUserName(user) {
if (!user || !user.name) {
return "Unknown";
}
if (typeof user.name !== "string") {
return "Unknown";
}
return user.name.toUpperCase();
}SyntaxError:语法错误
SyntaxError 表示代码的语法有问题,JavaScript 引擎无法解析:
// 缺少右括号
function test( {
console.log("hello");
}
// 非法的对象字面量
let obj = { a: 1, b: };
// 非法的 JSON
JSON.parse('{ invalid json }'); // SyntaxError: Unexpected token i in JSON大多数 SyntaxError 会在代码加载时就被检测到,而不是运行时。但动态执行的代码(如 eval 或 JSON.parse)可能在运行时抛出:
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 场景
// ❌ 意外的标识符
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. 无效的数组长度
let arr = new Array(-1); // RangeError: Invalid array length
let arr2 = new Array(4294967296); // RangeError: Invalid array length(太大)数组长度必须是非负整数,且小于 2^32。
2. 递归调用过深(栈溢出)
function recursiveFunction() {
recursiveFunction(); // 无限递归
}
recursiveFunction(); // RangeError: Maximum call stack size exceeded3. 数值方法的无效参数
let num = 123.456;
num.toFixed(-1); // RangeError: toFixed参数必须在 0 到 100 之间
num.toFixed(101); // RangeError
num.toPrecision(0); // RangeError: toPrecision 参数必须在 1 到 100 之间处理 RangeError
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 处理函数时传入无效参数会抛出:
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 不抛出 EvalError
eval("let x = 1"); // ✅ 正常工作
// 语法错误会抛出 SyntaxError
try {
eval("let x = ;");
} catch (error) {
console.log(error.name); // SyntaxError(不是 EvalError)
}因此,你几乎不需要处理 EvalError。
InternalError:内部错误(非标准)
InternalError 是一些浏览器(如 Firefox)的非标准扩展,表示 JavaScript 引擎内部错误:
// 可能导致 InternalError 的情况(仅 Firefox)
// - 过多的 switch case
// - 正则表达式过于复杂
// - 过大的数组初始化器由于这不是标准错误,不应该在跨浏览器的代码中依赖它。
AggregateError:聚合错误(ES2021)
AggregateError 用于表示多个错误的集合,常见于 Promise.any() 失败时:
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]
});也可以手动创建:
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");错误类型的实际应用
根据错误类型采取不同策略
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
}区分可恢复和不可恢复错误
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;
}
}
}详细的错误日志
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
try {
someRiskyOperation();
} catch (error) {
if (error instanceof TypeError) {
// 处理类型错误
} else if (error instanceof ReferenceError) {
// 处理引用错误
}
}检查 error.name
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
// ❌ 不可靠:错误消息可能改变或不同
if (error.message.includes("not defined")) {
// ...
}
// ✅ 使用错误类型
if (error instanceof ReferenceError) {
// ...
}总结
理解 JavaScript 的内置错误类型能帮助你快速诊断问题、采取正确的处理策略。每种错误类型都代表特定类别的问题,识别它们能让你的错误处理更精确、更有效。
关键要点:
- Error:所有错误的基类
- ReferenceError:访问不存在的变量
- TypeError:类型不匹配或对值进行不兼容的操作
- SyntaxError:代码语法错误
- RangeError:数值超出有效范围
- URIError:URI 处理函数的无效参数
- AggregateError:多个错误的集合
- 使用
instanceof检查错误类型 - 根据错误类型采取不同的处理策略
- 不要依赖
error.message进行判断
掌握这些错误类型,能让你在调试时更快定位问题,在编码时写出更健壮的错误处理逻辑。