onerror 回调抛出未捕获异常会再次触发自身,形成无限递归报错;应在外层加 try/catch 且仅执行同步无副作用操作,或临时设为 null 中断链路。

onerror 事件触发后为何会无限报错
浏览器中 onerror 回调一旦抛出未捕获异常,会再次触发全局 window.onerror,形成递归调用。这不是设计缺陷,而是错误传播机制的自然结果:JS 引擎把回调里新抛的错误当作另一个独立错误事件处理。
典型表现是控制台刷屏式报错,堆栈里反复出现同一行 onerror 处理函数,CPU 占用飙升,页面卡死。
- 检查你的
onerror回调是否包含可能失败的操作,比如调用未定义函数、访问null属性、或执行fetch但没加try/catch - 避免在
onerror中调用任何第三方 SDK 的上报方法(如 Sentry 的Sentry.captureException),它们内部可能触发额外 DOM 操作或 Promise reject - 最安全的做法是:在
onerror回调最外层包一层try/catch,且catch块内只做同步、无副作用的操作(如写入localStorage或设置一个布尔标记)
如何临时禁用 onerror 避免调试干扰
Chrome 和 Firefox 的开发者工具不提供“禁用全局错误监听器”的开关,但你可以快速覆盖它来中断循环。
在控制台直接执行以下代码即可立即切断链路:
立即学习“前端免费学习笔记(深入)”;
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
window.onerror = null;
或者更稳妥地保留原始逻辑但阻止重复触发:
const originalOnError = window.onerror;
window.onerror = function(...args) {
if (window._onerrorLocked) return true;
window._onerrorLocked = true;
try {
return originalOnError?.apply(this, args) !== false;
} finally {
window._onerrorLocked = false;
}
};
图片/脚本加载失败时的 onerror 不走 window.onerror?
HTML 元素级的 onerror(如 <img onerror="handleImgError()">)和全局 window.onerror 是两套机制。前者只响应该元素自身的加载失败,后者默认只捕获 JS 执行错误和部分资源加载错误(需配合 crossorigin)。
- 静态资源(图片、CSS、字体)加载失败不会触发
window.onerror,除非资源带crossorigin属性且服务器返回 CORS 头 -
<script>标签加载失败会触发window.onerror,但只有在设置了crossorigin且响应头合法时,才能拿到完整错误信息(否则只有"Script error.") - 调试图片加载失败,请优先检查元素自身的
onerror回调,或用 Network 面板过滤img请求看状态码
用 addEventListener 捕获错误比 onerror 更可控吗
是的。window.addEventListener('error') 和 window.addEventListener('unhandledrejection') 组合使用,能更细粒度地区分错误类型,也更容易移除监听器防止残留。
-
addEventListener支持多次绑定不同回调,而window.onerror是单值赋值,后赋的会覆盖前一个 - 可通过
removeEventListener精确清理,适合模块化开发或测试环境动态启停 - 注意:
error事件监听器必须在错误发生前注册;若在setTimeout中注册,可能错过初始脚本错误 - 推荐组合写法:
window.addEventListener('error', e => { if (e.error instanceof Error) { // JS 执行错误 } else if (e.filename && e.lineno) { // 资源加载错误(需 crossorigin) } }); window.addEventListener('unhandledrejection', e => { // Promise reject 未被捕获 });
真正麻烦的从来不是怎么写 onerror,而是当它开始自我繁殖时,你有没有在第一轮错误里就守住边界——所有外部调用都得兜底,所有异步操作都得隔离,否则调试器还没打开,页面已经凉了。


















