JavaScript 异常捕获完全指南
文章目录
- 一、基础结构:try...catch...finally
- 二、错误对象 Error
- 1. 内置错误类型
- 2. 自定义错误
- 三、主动抛出:throw
- 四、不同错误类型的精准捕获
- 五、finally 的妙用
- 六、异步异常捕获
- 1. Promise 的 catch
- 2. async/await + try/catch
- 3. 事件循环中的错误
- 七、全局异常捕获(兜底防线)
- 1. window.onerror
- 2. window.addEventListener('error')
- 3. unhandledrejection 事件
在开发中,错误不可避免——网络请求失败、用户输入非法、第三方脚本出错……如果放任不管,一个未捕获的异常就可能让整个应用白屏崩溃。异常捕获正是赋予我们“预判风险、优雅降级”的能力。JS 提供了丰富的错误处理手段,下面逐一解析。
一、基础结构:try…catch…finally
这是处理同步代码异常的核心语法。
try{// 尝试执行的代码letdata=JSON.parse(jsonStr);}catch(error){// 当 try 块中抛出错误时执行 console.error('解析失败:', error.message);}finally{// 无论是否出错,最终都会执行 console.log('清理临时变量、关闭连接等');}执行流程
1、执行 try 中的代码。
2、如果没有异常,跳过 catch,执行 finally。
3、如果抛出异常,try 中后续代码立即停止,进入 catch;执行完catch 后再进入 finally。
4、finally 即使在 try 或 catch 中有 return,也依然会执行(在真正返回前执行)。
注意:catch 捕获的必须是 try 块中同步执行时抛出的错误。若是异步回调中的错误(如 setTimeout),外层 try/catch 无能为力,后文会详解。
二、错误对象 Error
1. 内置错误类型
JS 内置了多种错误构造函数,可通过 instanceof 区分:
1、Error —— 通用错误
2、SyntaxError —— 语法错误
3、ReferenceError —— 引用错误(变量不存在)
4、TypeError —— 类型错误
5、RangeError —— 数值超出范围
6、URIError —— URI 处理函数错误
每种错误都包含 name、message、stack 等属性。stack 记录了调用栈,是调试的利器。
2. 自定义错误
扩展 Error 类可以创建业务专用错误,携带更多上下文信息。
class ApiError extends Error{constructor(message, statusCode){super(message);this.name='ApiError';this.statusCode=statusCode;}}try{throw new ApiError('Not Found',404);}catch(err){if(err instanceof ApiError){console.error(`API 错误 ${err.statusCode}: ${err.message}`);}}三、主动抛出:throw
任何值都可以被 throw(但推荐抛出 Error 对象),它能够中断当前执行流,并沿着调用栈向上传播,直到被 catch 捕获。
functiondivide(a, b){if(b===0){throw new Error('除数不能为零');}returna / b;}四、不同错误类型的精准捕获
catch 块可以结合 instanceof 或 error.name 来做分类处理。
try{// 可能抛出多种错误的代码}catch(err){if(err instanceof TypeError){// 处理类型错误}elseif(err instanceof RangeError){// 处理范围错误}else{// 其他未知错误重新抛出 throw err;}}重新抛出是一个重要技巧:只处理你预期的错误,未预料的错误继续向上抛给上层逻辑。
五、finally 的妙用
finally 适合放“必须执行”的清理代码,比如关闭文件流、清除定时器、隐藏 loading 效果等。即使 try 或 catch 中执行了 return,finally 也会在返回值真正交付之前运行。
functiongetUser(){showLoading();try{returnfetchData();// 即使这里 return,finally 仍会执行}catch(e){returndefaultData;}finally{hideLoading();// 必然执行,保证 loading 隐藏}}注意:如果 finally 中显式 return,它会覆盖 try/catch 中的返回值,需谨慎。
六、异步异常捕获
这是前端异常处理中最容易出问题的环节。
1. Promise 的 catch
Promise 内的错误(包括 reject 和运行时错误)都会被 catch 捕获,不会冒泡到外层同步 try/catch。
fetch('/api/user').then(res=>res.json()).then(data=>{// 如果这里抛出错误,会被后面的 catch 捕获 console.log(data.info);}).catch(err=>{console.error('请求失败:', err);});2. async/await + try/catch
async 函数使异步代码看起来像同步,因此 try/catch 可以直接捕获 await 表达式的异常。
asyncfunctionloadUser(){try{const response=await fetch('/api/user');const data=await response.json();returndata;}catch(err){console.error('加载用户失败', err);// 返回降级数据或重新抛出return{name:'匿名用户'};}}重要:await 会将 Promise 的 reject 转化为同步异常,所以 try/catch 有效。但别忘了 async 函数自身返回的是 Promise,调用方也需要处理可能的异常。
3. 事件循环中的错误
setTimeout、setInterval 等回调在下一个事件循环中执行,外层的 try/catch 完全无效。
try{setTimeout(()=>{throw new Error('定时器错误');// 全局异常,外层 try 捕获不到},1000);}catch(e){// 不会执行}正确的做法是在回调内部自行 try/catch,或依赖全局异常捕获机制。
七、全局异常捕获(兜底防线)
即使项目代码再完善,仍可能有遗漏的未捕获异常。浏览器提供了全局监听手段,用于错误上报与友好提示。
1. window.onerror
捕获全局同步运行时错误(除 Promise 外)。
window.onerror=function(message, source, lineno, colno, error){console.error('全局错误捕获:', message,'at', source, lineno);// 上报到监控平台returntrue;// 返回true可阻止浏览器默认报错行为};2. window.addEventListener(‘error’)
更强大的监听,可以捕获资源加载失败(如 404、脚本加载失败)以及运行时错误(比 onerror 稍晚触发,但信息更全)。
window.addEventListener('error',(event)=>{if(event.target instanceof HTMLImageElement){console.error('图片加载失败:', event.target.src);}else{console.error('运行时错误:', event.message);}},true);// 使用捕获阶段,因为资源错误不冒泡3. unhandledrejection 事件
专用于捕获未被 .catch() 或 try/catch 处理的 Promise 拒绝。
window.addEventListener('unhandledrejection', event=>{console.error('未处理的 Promise 拒绝:', event.reason);// 可以调用 event.preventDefault()防止控制台默认报错});现代前端监控体系通常会结合这三种全局事件 + 框架级错误边界(如 React Error Boundary)来实现完善的异常收集。
