利用 Object.defineProperty 解决 window.onload 覆盖问题
作者:代码拾荒时间:2026-10-23 14:27:14浏览:183
在老项目维护中,多个脚本加载可能导致 window.onload 回调被覆盖。本文介绍一种利用 Object.defineProperty 劫持赋值行为的方案,将回调函数存入队列集中处理,无需修改原有打包代码即可防止后续加载覆盖先前的 onload 逻辑。
问题背景与常见方案局限
在维护老旧项目时,由于页面结构重组,不同页面的 JavaScript 代码可能被加载到同一页面中。这导致一个页面内存在多个 window.onload 定义,其直接后果是前面的回调函数会被后续的赋值操作覆盖掉。
- 常见的解决方案主要有两种:
- 一是重载 window.onload 方法;二是推荐使用 window.addEventListener。
然而,上述方法要么仅能预防新覆盖,要么需要修改原有代码。由于本项目中的老代码已通过 webpack 打包且配置文件遗失,维护人员曾直接修改打包后的代码,重新打包的工作量巨大,因此只能采取渐进式修改策略。
基于 Object.defineProperty 的解决方案
经过权衡,决定采用 Object.defineProperty 来劫持 window.onload 的赋值行为,将对应的回调函数收集到一个队列中,最后统一执行处理。
具体实现代码如下:
核心实现代码
/**
* @function windowLoadInit - 劫持 window.onload 的赋值行为,防止覆盖
* @desc 函数调用前产生的覆盖不可逆转
* @throw {Any} 所有回调执行完毕之后,会抛出 catch 到的第一个错误
* 错误将被异步抛出,避免影响初始化函数的继续执行
* @return {Function}
*/
function windowLoadInit(){
const eventQueue = [];
// 防止覆盖之前的 window.onload
window.onload instanceof Function && eventQueue.push(window.onload);
window.onload = e => {
const errQueue = [];
// 逐个处理回调事件
while(!!eventQueue.length){
try{
eventQueue.shift()(e);
} catch(err){
errQueue.push(err);
}
}
if(!!errQueue.length) {
setTimeout(() => {
throw errQueue.shift();
},0);
};
};
// 每次赋值时,将回调函数添加到队列
Object.defineProperty(window, 'onload', {
set: eventQueue.push
});
return window.onload
}
测试代码如下:
测试用例验证
// 此 testCase 需在页面加载完成前执行
function testCase(){
const arr = [];
window.onload = () => arr.push(-1); // 这个回调的覆盖不可避免
window.onload = () => arr.push(0);
windowLoadInit();
window.onload = () => arr.push(1);
window.onload = () => arr.push(2);
return new Promise(resolve => {
window.onload = () => resolve(arr.join('') === '012')
})
}
testCase().then(console.log); // true
以上即为本文的全部内容,希望能为您的学习提供有益参考,欢迎继续支持本站。
作者最新文章
JavaScript入门书籍避坑:这些书不适合初学者
2026-11-05 16:31:15
JS使用Math.round实现保留两位小数的原理与代码
2026-11-05 15:35:55
JavaScript 原生 Date 对象怎么实现日期加减
2026-11-05 14:30:00
JavaScript函数定义的三种方式:声明、表达式与箭头函数
2026-11-05 13:50:55
JavaScript中escape、encodeURI和encodeURIComponent对空格的处理差异
2026-11-05 12:47:23
