异常处理

throw语句

throw语句用于抛出一个异常。

基本用法

throw "错误信息";

throw new Error("错误信息");

throw {
    name: "自定义错误",
    message: "错误详情"
};

抛出不同类型的值

// 抛出字符串
function divide(a, b) {
    if (b === 0) {
        throw "除数不能为零";
    }
    return a / b;
}

// 抛出Error对象
function validateAge(age) {
    if (age < 0) {
        throw new Error("年龄不能为负数");
    }
    return age;
}

// 抛出自定义错误
class ValidationError extends Error {
    constructor(message) {
        super(message);
        this.name = "ValidationError";
    }
}

function validateUser(user) {
    if (!user.name) {
        throw new ValidationError("用户名不能为空");
    }
}

抛出时机

// 参数验证
function setAge(age) {
    if (typeof age !== "number") {
        throw new TypeError("年龄必须是数字");
    }
    if (age < 0 || age > 150) {
        throw new RangeError("年龄必须在0-150之间");
    }
    this.age = age;
}

// 状态检查
class Connection {
    constructor() {
        this.connected = false;
    }
    
    send(data) {
        if (!this.connected) {
            throw new Error("连接未建立");
        }
        console.log("发送数据:", data);
    }
}

try-catch语句

try-catch语句用于捕获和处理异常。

基本语法

try {
    // 可能出错的代码
} catch (error) {
    // 处理错误
}

// 示例
try {
    let result = divide(10, 0);
} catch (error) {
    console.log("发生错误:", error);
}

catch块

try {
    JSON.parse("无效的JSON");
} catch (e) {
    console.log(e.name);     // SyntaxError
    console.log(e.message);  // 错误详情
    console.log(e.stack);    // 错误堆栈
}

处理不同类型的错误

try {
    // 可能抛出多种错误的代码
    let data = JSON.parse(input);
    process(data);
} catch (error) {
    if (error instanceof SyntaxError) {
        console.log("JSON格式错误:", error.message);
    } else if (error instanceof TypeError) {
        console.log("类型错误:", error.message);
    } else if (error instanceof RangeError) {
        console.log("范围错误:", error.message);
    } else {
        console.log("未知错误:", error.message);
    }
}

嵌套try-catch

try {
    try {
        throw new Error("内部错误");
    } catch (e) {
        console.log("内部捕获:", e.message);
        throw e;  // 重新抛出
    }
} catch (e) {
    console.log("外部捕获:", e.message);
}

finally语句

finally块无论是否发生异常都会执行。

基本语法

try {
    // 可能出错的代码
} catch (error) {
    // 处理错误
} finally {
    // 无论是否出错都会执行
}

// 示例
function readFile(path) {
    let file = openFile(path);
    try {
        return file.read();
    } finally {
        file.close();  // 确保关闭文件
    }
}

执行顺序

function test() {
    try {
        console.log("try");
        return "返回值";
    } catch (e) {
        console.log("catch");
    } finally {
        console.log("finally");
    }
}

console.log(test());
// try
// finally
// 返回值

finally与return

// finally的return会覆盖try的return
function test() {
    try {
        return "try返回";
    } finally {
        return "finally返回";
    }
}

console.log(test());  // "finally返回"

// 建议:不要在finally中使用return

资源清理

function processResource() {
    let resource = acquireResource();
    try {
        resource.doSomething();
        resource.doAnother();
    } catch (e) {
        console.log("处理出错:", e);
    } finally {
        resource.release();  // 确保释放资源
    }
}

Error对象

JavaScript内置了多种错误类型。

Error类型

错误类型 说明 示例场景
Error 通用错误 自定义错误
SyntaxError 语法错误 JSON解析失败
TypeError 类型错误 调用不存在的方法
RangeError 范围错误 递归溢出
ReferenceError 引用错误 访问未定义变量
URIError URI错误 encodeURI参数错误

创建Error对象

// 基本创建
let error1 = new Error("出错了");
let error2 = Error("出错了");  // 可以省略new

// 带文件名和行号
let error3 = new Error("出错了", "script.js", 10);

// 访问属性
console.log(error1.name);     // "Error"
console.log(error1.message);  // "出错了"
console.log(error1.stack);    // 堆栈信息

内置错误示例

// SyntaxError
eval("无效代码");  // SyntaxError

// TypeError
null.foo();  // TypeError: Cannot read property 'foo' of null

// RangeError
function recurse() { recurse(); }
recurse();  // RangeError: Maximum call stack size exceeded

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

// URIError
decodeURI("%");  // URIError: URI malformed

自定义错误类型

通过继承Error创建自定义错误。

基本自定义错误

class CustomError extends Error {
    constructor(message) {
        super(message);
        this.name = "CustomError";
    }
}

try {
    throw new CustomError("自定义错误信息");
} catch (e) {
    console.log(e.name);     // "CustomError"
    console.log(e.message);  // "自定义错误信息"
}

带额外属性的错误

class HttpError extends Error {
    constructor(message, statusCode) {
        super(message);
        this.name = "HttpError";
        this.statusCode = statusCode;
    }
}

try {
    throw new HttpError("资源未找到", 404);
} catch (e) {
    console.log(e.name);        // "HttpError"
    console.log(e.message);     // "资源未找到"
    console.log(e.statusCode);  // 404
}

实用自定义错误

// 验证错误
class ValidationError extends Error {
    constructor(message, field) {
        super(message);
        this.name = "ValidationError";
        this.field = field;
    }
}

function validateUser(user) {
    if (!user.name) {
        throw new ValidationError("用户名必填", "name");
    }
    if (!user.email) {
        throw new ValidationError("邮箱必填", "email");
    }
}

// 数据库错误
class DatabaseError extends Error {
    constructor(message, query) {
        super(message);
        this.name = "DatabaseError";
        this.query = query;
    }
}

// 权限错误
class PermissionError extends Error {
    constructor(message, requiredRole) {
        super(message);
        this.name = "PermissionError";
        this.requiredRole = requiredRole;
    }
}

异常处理最佳实践

1. 只捕获你能处理的错误

// 不推荐:捕获所有错误但不处理
try {
    doSomething();
} catch (e) {
    // 什么都不做
}

// 推荐:只捕获需要处理的错误
try {
    doSomething();
} catch (e) {
    if (e instanceof SpecificError) {
        handleSpecificError(e);
    } else {
        throw e;  // 重新抛出无法处理的错误
    }
}

2. 提供有意义的错误信息

// 不推荐
throw new Error("错误");

// 推荐
throw new Error(`用户${userId}不存在,请检查用户ID是否正确`);

3. 使用自定义错误类型

// 不推荐
if (!user) {
    throw new Error("用户不存在");
}

// 推荐
class UserNotFoundError extends Error {
    constructor(userId) {
        super(`用户${userId}不存在`);
        this.name = "UserNotFoundError";
        this.userId = userId;
    }
}

if (!user) {
    throw new UserNotFoundError(userId);
}

4. 记录错误日志

try {
    await processPayment(order);
} catch (e) {
    logger.error("支付处理失败", {
        orderId: order.id,
        error: e.message,
        stack: e.stack
    });
    notifyUser("支付失败,请重试");
}

5. 异步错误处理

// Promise
fetch(url)
    .then(response => response.json())
    .catch(error => console.error("请求失败:", error));

// async/await
async function fetchData() {
    try {
        const response = await fetch(url);
        return await response.json();
    } catch (error) {
        console.error("请求失败:", error);
        throw error;
    }
}

6. 错误边界

function safeExecute(fn, fallback) {
    try {
        return fn();
    } catch (e) {
        console.error("执行出错:", e);
        return fallback;
    }
}

const result = safeExecute(
    () => JSON.parse(data),
    {}  // 解析失败时返回空对象
);

下一步

掌握了异常处理后,让我们继续学习:

  1. 函数基础 - 学习函数定义与调用
  2. 箭头函数 - 学习箭头函数
  3. 作用域与闭包 - 学习作用域

东巴文(db-w.cn) - 让编程学习更简单

⚠️ 东巴文寄语:异常处理是程序健壮性的保障,合理的异常处理能让程序在遇到意外情况时优雅地处理,而不是直接崩溃。在 db-w.cn,我们教你写出更健壮的代码!