← JavaScript Cookie删除 JavaScript Map →

JavaScript 异常处理

原创 2026-03-23 JavaScript 已有人查阅

JavaScript异常处理:从入门到优雅应对错误

在编程的世界里,没有任何一个复杂的程序能保证不犯错。我们常说的“异常”(Exception),其实就是程序在运行过程中遇到的、打乱了正常逻辑流程的“意外状况”。在JavaScript中,处理好这些异常,不仅仅是让代码不崩溃,更是一种专业素养的体现。它能让我们的程序在面对未知情况时,依然能保持一定的稳定性和可维护性。

我自己刚开始写前端时,最怕看到的就是页面突然一片空白,控制台报一个Uncaught TypeError。那时候的经验就是,处理异常能让你从“被动填坑”变成“主动掌控”。

什么是JavaScript中的错误与异常?

在聊怎么处理之前,我们需要先搞清楚JavaScript里有哪些“捣蛋鬼”。

错误的三种类型

  1. 语法错误 (Syntax Error)
    这属于较低级的错误,是你写的代码不符合JavaScript的语言规则。比如少写了一个括号、漏了一个分号。这种错误通常会在代码解析阶段就被发现,导致整个脚本无法执行。

    // 代码号学习编程提醒:下面的语句会在解析时直接报错
    // var a = ; 
    // 浏览器根本不会执行后续任何代码,因为这句话本身就不合法。
  2. 运行时错误 (Runtime Error)
    这是我们需要重点关注的对象,通常被称为异常(Exception)。语法没问题,但在执行时出了岔子。比如试图调用一个不存在的方法,或者访问一个未定义的变量。这类错误不会直接让整个程序“死掉”,但如果没处理好,它会像一颗,炸毁当前执行的代码块,并向上抛出错误信息。

  3. 逻辑错误 (Logical Error)
    这是最隐蔽、最让人头疼的错误。代码能跑,没有报错,但结果就是不对。比如你写了一个计算折扣的函数,结果算出来的价格比原价还高。这种错误只能靠更完善的测试和清晰的逻辑设计来避免。

认识Error对象

当运行时错误发生时,JavaScript会主动创建一个Error对象。这个对象是我们处理异常的基石。它有两个核心属性:

  • name: 错误类型,比如TypeError

  • message: 具体的错误描述,告诉我们哪里出问题了。

JavaScript内置了多种标准错误类型,我们可以利用它们来区分不同的异常场景:

  • SyntaxError: 解析代码时发生语法错误。

    try {
        // 代码号学习编程示例:eval中包含了非法语句
        eval('console.log("你好"'); // 这里少了一个括号
    } catch (error) {
        console.log(error.name);     // 输出: SyntaxError
        console.log(error.message);  // 输出: missing ) after argument list
    }
  • RangeError: 数值超出了有效范围。

    function validateNum(num) {
        if (num < 0 || num > 100) {
            // 抛出一个自定义的范围错误
            throw new RangeError('数值必须在0到100之间');
        }
        return true;
    }
    
    try {
        validateNum(150);
    } catch (e) {
        console.log(e.name);      // 输出: RangeError
        console.log(e.message);   // 输出: 数值必须在0到100之间
    }
  • ReferenceError: 引用了一个不存在的变量。

    try {
        console.log(someUndefinedVar);
    } catch (e) {
        console.log(e.name); // 输出: ReferenceError
        // 个人见解:这种错误通常是由于拼写错误或作用域理解不清造成的
        console.log(e.message); 
    }
  • TypeError: 变量或参数不是预期类型。

    let num = 123;
    try {
        // 数字没有 split 方法,这是个典型的类型错误
        num.split('');
    } catch (e) {
        console.log(e.name); // 输出: TypeError
        console.log(e.message); 
    }
  • 自定义错误: 除了系统错误,我们还可以根据业务需求抛出自己的错误。这让错误信息更有针对性,比如“用户不存在”、“库存不足”等。

    function login(username) {
        if (!username) {
            // 代码号学习编程建议:自定义错误让调试更友好
            throw new Error('用户名不能为空,这是应用层面的业务规则');
        }
        // ... 登录逻辑
    }
    
    try {
        login('');
    } catch (err) {
        console.log(err.message); // 输出: 用户名不能为空,这是应用层面的业务规则
    }

核心异常处理武器:try...catch...finally

try...catch...finally 是JavaScript处理异常的核心语法结构。它就像给一段可能会出问题的代码穿上了一层“防护服”。

  • try 块: 将可能抛出异常的代码放在这里。

  • catch 块: 如果try块中发生了异常,代码会立刻跳转到catch块,并将错误对象作为参数传进来。

  • finally 块: 无论是否发生异常,finally块中的代码都会执行。这是一个非常实用的特性,常用于清理工作,比如关闭文件流、清除计时器、隐藏加载动画等。

本节课程知识要点finally是保证资源释放的关键,即使trycatch中有return语句,finally也会在return执行之前先执行。

function divide(a, b) {
    try {
        // 重点监控区域
        if (b === 0) {
            // 主动抛出错误
            throw new Error('除数不能为零');
        }
        return a / b;
    } catch (error) {
        console.error('发生错误:', error.message);
        // 根据实际情况,这里可以返回一个默认值,或者重新抛出错误
        return null; 
    } finally {
        // 个人经验分享:这里很适合做日志记录或资源回收
        console.log('除法运算尝试结束,无论成功与否,这条信息都会输出');
    }
}

console.log(divide(10, 2)); // 输出: 5, 并打印 finally 信息
console.log(divide(10, 0)); // 输出: 发生错误: 除数不能为零, null, 并打印 finally 信息

主动出击:throw语句

throw 语句让我们可以主动制造异常。它就像一个自定义的“报警器”,当我们的代码检测到业务逻辑上的违规时,就可以触发它。

你可以抛出任何值,比如数字、字符串,但实践是抛出Error对象或其子类的实例。因为Error对象携带了namemessage等标准信息,使得后续的catch块能更准确地处理。

function deposit(amount) {
    if (amount <= 0) {
        // 使用 Error 对象,而不是抛出 '金额必须为正数' 这样简单的字符串
        // 这样在 catch 中就能通过 instanceof 进行判断
        throw new Error('存款金额必须大于0');
    }
    console.log(`已存入 ${amount} 元`);
}

try {
    deposit(-100);
} catch (err) {
    // 判断错误类型,可以做更精细的处理
    if (err instanceof Error) {
        console.error('操作失败:', err.message);
    }
}

处理异步代码中的异常

在异步编程中,传统的 try...catch 处理方式需要留意。对于基于Promise的异步操作,我们通常使用 .catch() 方法。而在async/await语法中,try...catch又变得非常自然和直观。

个人见解:我更喜欢用async/await配合try...catch,因为它让异步代码看起来就像同步代码一样,逻辑清晰,异常处理也更集中。

// 模拟一个可能出错的异步请求
function fetchUserData(userId) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (userId === 1) {
                resolve({ name: '张三' });
            } else {
                // 模拟一个请求失败的情况
                reject(new Error('用户不存在'));
            }
        }, 1000);
    });
}

async function displayUser(userId) {
    try {
        // 代码号学习编程:在 await 外包裹 try-catch,错误会被捕获
        const user = await fetchUserData(userId);
        console.log('用户信息:', user);
    } catch (error) {
        console.error('获取用户信息时出错:', error.message);
        // 这里可以展示一个友好的UI提示给用户
    }
}

displayUser(2); // 输出: 获取用户信息时出错: 用户不存在
displayUser(1); // 输出: 用户信息: { name: '张三' }

全局异常兜底:window.onerror

即使我们小心翼翼地处理了每一个可能的异常,总有一些“漏网之鱼”会逃过try...catch的捕获。这时,window.onerror事件就成了之后一道防线。它能捕获那些未被处理的运行时错误,让我们有机会记录日志或给用户一个友好的提示,而不是让程序直接“白屏”。

window.onerror = function(message, source, lineno, colno, error) {
    // 个人经验分享:在这里,你可以将错误信息通过 AJAX 发送到后端日志服务器
    console.log('【全局捕获】未处理的错误:', message, '在文件', source, '第', lineno, '行');
    // 返回 true 可以阻止浏览器默认的错误处理(比如控制台报错),通常返回 false 即可
    return false;
};

// 模拟一个未被 try-catch 包裹的错误
function causeError() {
    someUndefinedFunction(); // ReferenceError,会被 window.onerror 捕获
}
causeError();

本节课程知识要点window.onerror虽然强大,但它无法捕获Promise中的reject错误。要捕获未被处理的Promise异常,需要监听unhandledrejection事件。

window.addEventListener('unhandledrejection', function(event) {
    console.error('【全局捕获】未处理的 Promise 错误:', event.reason);
    // 可以选择阻止事件冒泡
    event.preventDefault();
});

// 一个未被处理的 Promise rejection
new Promise((resolve, reject) => {
    reject(new Error('这是一个未处理的 Promise 异常'));
});

异常处理不是可有可无的“加分项”,而是一种负责任的设计方式。它能提升代码的健壮性,改善用户体验,也为我们自己节省了大量的调试时间。

当你在编写函数时,不妨多思考一下:这里可能会出什么错?如果出错了,我期望程序如何响应?是返回一个默认值,还是重试,还是优雅地告知用户?将这些思考变成throwtry...catch,你的代码会变得更加专业和可靠。

← JavaScript Cookie删除 JavaScript Map →
分享笔记 (共有 篇笔记)
验证码:
微信公众号