错误处理

错误类型

JavaScript中的错误类型。

内置错误类型

// Error: 基础错误类型
throw new Error("通用错误");

// SyntaxError: 语法错误
// throw new SyntaxError("语法错误");

// ReferenceError: 引用错误
// throw new ReferenceError("变量未定义");

// TypeError: 类型错误
throw new TypeError("类型错误");

// RangeError: 范围错误
throw new RangeError("超出范围");

// URIError: URI错误
throw new URIError("URI编码错误");

// EvalError: eval错误(已废弃)

自定义错误

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

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

// 使用
function validateUser(user) {
    if (!user.name) {
        throw new ValidationError("名称不能为空", "name");
    }
    
    if (!user.email) {
        throw new ValidationError("邮箱不能为空", "email");
    }
}

throw语句

抛出错误。

基本用法

// 抛出错误
throw new Error("出错了");

// 抛出任意值
throw "错误字符串";
throw { message: "错误对象" };
throw 123;

// 推荐使用Error对象
throw new Error("错误信息");

条件抛出

function divide(a, b) {
    if (b === 0) {
        throw new Error("除数不能为0");
    }
    return a / b;
}

function getAge(age) {
    if (typeof age !== "number") {
        throw new TypeError("年龄必须是数字");
    }
    
    if (age < 0 || age > 150) {
        throw new RangeError("年龄必须在0-150之间");
    }
    
    return age;
}

try-catch-finally

捕获和处理错误。

try-catch

try {
    // 可能出错的代码
    const result = JSON.parse('{"name": "东巴文"');
} catch (error) {
    // 处理错误
    console.error(error.name);    // SyntaxError
    console.error(error.message); // 错误信息
    console.error(error.stack);   // 错误堆栈
}

finally

try {
    // 可能出错的代码
    const data = fetchData();
    processData(data);
} catch (error) {
    console.error(error);
} finally {
    // 无论是否出错都执行
    cleanup();
}

// 实际应用
let connection;
try {
    connection = openConnection();
    const data = connection.read();
    return processData(data);
} catch (error) {
    logError(error);
    throw error;
} finally {
    if (connection) {
        connection.close();
    }
}

嵌套try-catch

try {
    try {
        throw new Error("内部错误");
    } catch (innerError) {
        console.log("捕获内部错误:", innerError.message);
        throw new Error("外部错误");
    }
} catch (outerError) {
    console.log("捕获外部错误:", outerError.message);
}

错误传播

错误的传播机制。

同步错误传播

function a() {
    throw new Error("a中的错误");
}

function b() {
    a();
}

function c() {
    b();
}

try {
    c();
} catch (error) {
    console.log(error.message);  // "a中的错误"
}

异步错误传播

// 回调中的错误
function asyncOperation(callback) {
    setTimeout(() => {
        try {
            const result = riskyOperation();
            callback(null, result);
        } catch (error) {
            callback(error);
        }
    }, 1000);
}

// Promise中的错误
async function fetchUser() {
    try {
        const response = await fetch("/api/user");
        return response.json();
    } catch (error) {
        console.error("获取用户失败:", error);
        throw error;
    }
}

// 事件处理中的错误
process.on("uncaughtException", (error) => {
    console.error("未捕获的异常:", error);
});

process.on("unhandledRejection", (reason, promise) => {
    console.error("未处理的Promise拒绝:", reason);
});

错误处理模式

常见的错误处理模式。

返回错误对象

// 返回 { error, data } 模式
function divide(a, b) {
    if (b === 0) {
        return { error: new Error("除数不能为0"), data: null };
    }
    return { error: null, data: a / b };
}

const { error, data } = divide(10, 0);
if (error) {
    console.error(error);
} else {
    console.log(data);
}

Result类型

// Result类型模式
class Result {
    static ok(value) {
        return { ok: true, value };
    }
    
    static err(error) {
        return { ok: false, error };
    }
}

function divide(a, b) {
    if (b === 0) {
        return Result.err("除数不能为0");
    }
    return Result.ok(a / b);
}

const result = divide(10, 2);
if (result.ok) {
    console.log(result.value);
} else {
    console.error(result.error);
}

错误边界

// 错误边界组件(React风格)
class ErrorBoundary {
    constructor() {
        this.hasError = false;
        this.error = null;
    }
    
    try(fn) {
        try {
            const result = fn();
            return result;
        } catch (error) {
            this.hasError = true;
            this.error = error;
            this.onError(error);
            return null;
        }
    }
    
    onError(error) {
        console.error("错误边界捕获:", error);
    }
    
    reset() {
        this.hasError = false;
        this.error = null;
    }
}

// 使用
const boundary = new ErrorBoundary();
boundary.try(() => {
    throw new Error("测试错误");
});

全局错误处理

处理未捕获的错误。

浏览器全局错误

// 全局错误处理
window.onerror = function(message, source, lineno, colno, error) {
    console.error("全局错误:", message);
    console.error("文件:", source);
    console.error("行号:", lineno);
    return true;  // 阻止默认行为
};

// Promise未处理拒绝
window.addEventListener("unhandledrejection", function(event) {
    console.error("未处理的Promise拒绝:", event.reason);
    event.preventDefault();
});

// 资源加载错误
window.addEventListener("error", function(event) {
    if (event.target !== window) {
        console.error("资源加载失败:", event.target.src);
    }
}, true);

Node.js全局错误

// 未捕获异常
process.on("uncaughtException", (error) => {
    console.error("未捕获的异常:", error);
    // process.exit(1);
});

// 未处理的Promise拒绝
process.on("unhandledRejection", (reason, promise) => {
    console.error("未处理的Promise拒绝:", reason);
});

// 警告
process.on("warning", (warning) => {
    console.warn("警告:", warning);
});

错误日志

记录和管理错误。

错误日志实现

class ErrorLogger {
    constructor() {
        this.errors = [];
    }
    
    log(error, context = {}) {
        const entry = {
            timestamp: new Date().toISOString(),
            name: error.name,
            message: error.message,
            stack: error.stack,
            context,
            url: window.location.href,
            userAgent: navigator.userAgent
        };
        
        this.errors.push(entry);
        this.sendToServer(entry);
        
        return entry;
    }
    
    sendToServer(entry) {
        fetch("/api/logs/error", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(entry)
        }).catch(console.error);
    }
    
    getErrors() {
        return this.errors;
    }
    
    clear() {
        this.errors = [];
    }
}

const logger = new ErrorLogger();

// 使用
try {
    riskyOperation();
} catch (error) {
    logger.log(error, { operation: "riskyOperation" });
}

下一步

掌握了错误处理后,让我们继续学习:

  1. 调试技术 - 学习调试技巧
  2. 性能优化基础 - 学习性能优化
  3. JavaScript优化 - 学习代码优化

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

🎯 东巴文寄语:良好的错误处理是健壮应用的基础,理解错误类型、正确使用try-catch、实现全局错误处理是每个开发者的必备技能。在 db-w.cn,我们帮你构建可靠的JavaScript应用!