凌晨3点,你的手机还在震。用户截图里一片白屏,控制台血红一片——这种场景,每个前端都经历过。
但多数人不知道:JavaScript自带的错误处理三件套(try/catch/finally),能把这种噩梦变成"用户无感知恢复"。问题是你真的用对了吗?
90%的崩溃,死于同一种错误
先认清敌人。JS错误分两类:语法错误(SyntaxError)在代码运行前就被拦截,而运行时错误(Runtime Error)才是线上杀手。
最经典的三种死法:
ReferenceError:直接调用未声明变量,像伸手进空口袋抓东西。
TypeError:给数字调字符串方法,好比让冰箱唱歌——对象根本没这功能。
RangeError:创建长度为-1的数组,数学上就不成立,程序直接掀桌。
这些错误不处理,执行流当场中断。用户看到白屏,你收到警报,所有人都不好过。
try/catch不是万能药,用错地方更糟
很多人把try/catch当护身符到处套,结果代码臃肿、性能损耗,还掩盖了真正该修的bug。
正确的打开方式:只包裹"可能失败且失败可接受"的操作。网络请求、用户输入解析、第三方API调用——这些属于"外部不可控",值得保护。
看个实战案例。fetch用户数据时,网络抖动、服务器500、JSON解析失败都可能发生:
async function fetchUserData(userId) { try { const response = await fetch(`/api/users/${userId}`); if (!response.ok) throw new Error("User not found"); const data = await response.json(); renderUserProfile(data); } catch (error) { console.error("Fetch failed:", error); displayFallbackUI("Couldn't load profile. Showing demo data instead."); } }
关键点在catch里的动作:不是默默吞掉错误,而是给用户可见的降级方案。demo数据、重试按钮、离线提示——让应用"带病运行"比直接死掉强十倍。
catch块还藏着一个调试神器:完整的错误对象包含堆栈追踪(stack trace)、错误信息、行号。线上环境配合日志上报,复现bug的速度从小时级降到分钟级。
finally:那个被80%的人遗忘的收尾者
finally的设计很朴素:无论try成功还是catch触发,这里的代码必定执行。像出门不管下不下雨,钥匙总要带。
典型场景是资源清理。加载动画要停、数据库连接要关、全局状态要复位——这些收尾动作如果散落在try和catch里,漏掉的概率极高。
但finally有个反直觉的坑:它自己抛出的错误会覆盖前面的错误。如果finally里写了个不靠谱的清理操作,原本要捕获的错误反而被吞掉,调试时你会怀疑人生。
另一个误区是在finally里return。这会强制覆盖try/catch的返回值,导致你以为返回了A,实际拿到的是B。这种bug隐蔽到能浪费你整个下午。
现代JS的错误处理,已经进化了
Promise时代带来了.catch()链式调用,async/await又让代码看起来像同步。但本质没变:错误处理是架构设计,不是语法糖。
一个趋势值得注意:可选链操作符(?.)和空值合并(??)减少了大量"防御性代码"。以前要try/catch防的属性访问,现在一行表达式搞定。但这不意味着try/catch过时——异步操作的错误边界, still 需要它兜底。
更激进的方案是错误边界(Error Boundary)模式,React里已经成熟。把UI组件包裹在错误边界中,子树崩溃不会拖垮整页。这和try/catch形成互补:前者保体验,后者保逻辑。
你的代码库里,有多少裸奔的fetch、没处理的JSON.parse、假设永远存在的DOM节点?下次发版前扫一遍,数字可能会让你坐不住。
热门跟贴