try-catch 会强制 v8 标记函数为不可优化,使其无法进入 turbofan 编译;应将核心逻辑抽离至无 try-catch 的独立函数,并在上层处理异常。

try-catch 块会让 V8 直接放弃对该函数使用 TurboFan 编译器优化,哪怕只包了一行赋值语句。这不是性能开销问题,而是优化路径被硬性阻断。
别让 try-catch 出现在热函数内部
V8 当前(v12.x 起)对含 try/catch 的函数整体标记为 not optimizable,无论 catch 是否为空、异常是否真会发生。一旦函数被标记,就无法进入 TurboFan 的优化流水线,只能长期停留在解释执行或轻量级的 Ignition 阶段。
- 错误写法:把数值计算、数组遍历、对象属性读取等高频逻辑写在 try 块里
- 正确做法:将核心逻辑抽成独立函数,确保它不被任何 try/catch 包裹
- 异常处理移到上层调用处,比如业务入口、事件回调或 API 封装层
避免在循环体中包裹整个 try-catch
for 或 while 循环是 V8 最常触发 OSR(栈上替换)和循环优化(如展开、向量化)的位置。整个循环被 try 包住后,V8 必须生成更复杂的异常表,且无法安全地做控制流分析,最终跳过深度优化。
- 禁止:
try { for (let i = 0; i - 允许:
for (let i = 0; i ,其中 <code>processItem是纯净函数 - 更优:在
processItem内部用防御性检查替代异常捕获(例如提前校验索引、类型、null 状态)
警惕隐式包裹:回调传参也受影响
即使函数本身没写 try/catch,若它作为回调被传入一个带 try 的高频调用位置(如 arr.map(fn)),而 fn 又恰好在 try 块内被调用,整个调用链可能降级——V8 会把该函数视作“可能处于异常上下文”,限制其优化机会。
- 不要把需高性能执行的函数定义在 try 块作用域内(包括函数表达式、箭头函数)
- 高频回调尽量声明在顶层或模块级,确保其闭包环境干净
- 避免用
eval、with或动态import()引入的代码间接污染调用链
替代方案比“吞异常”更有效
很多开发者加 try/catch 是为了防止某个字段访问失败或类型错误中断流程。但比起捕获再忽略,更稳定高效的方式是:
- 用
?.(可选链)和??(空值合并)替代try { x.y.z } catch - 用
Array.isArray()、Number.isFinite()等确定性检测替代运行时抛错 - 对第三方输入做一次预处理(如 schema 校验),后续流程就可按可信数据假设执行










