防止window.onload被覆盖的技巧

使用 window.onload 时,如果多次赋值,后面的赋值会覆盖前面的,导致只有一个函数会在页面加载完成后执行。这是因为 onload 是一个属性,而不是一个事件监听器集合,所以多次赋值会导致之前的函数被替换。

解决办法:

为了避免使用 window.onload 时覆盖之前的加载事件处理函数,可以通过检查 window.onload 是否已存在,并在其基础上追加新的处理逻辑,像这样:

window.onload = (function(originalOnLoad) {
    return function() {
        if (typeof originalOnLoad === 'function') {
            originalOnLoad(); // 执行原有的 onload 函数
        }
        // 这里写你的代码
        console.log("新添加的加载完成逻辑");
    };
})(window.onload);

这段代码使用了一个立即执行的匿名函数,它接收当前的 window.onload 函数作为参数(如果有的话),然后返回一个新的函数,这个新函数在执行时会先调用原始的 onload 函数(如果存在),随后执行你新添加的逻辑。这样就实现了加载事件处理函数的叠加,而不是覆盖。

这种方法在需要向页面添加第三方脚本或库,同时又不希望影响原有页面的 onload 处理逻辑时非常有用。不过,更现代和推荐的做法是使用 window.addEventListener("load", ...),它天然支持多个事件处理函数,无需担心覆盖问题。

但,怎么保证是你覆盖别人,而不是别人覆盖你呢?

要确保你的代码段是最后一个执行的 window.onload 处理函数,即使其他脚本之后尝试再次修改它,你可以使用一个立即执行函数表达式(IIFE)结合一个闭包来"劫持"或"包裹"现有的 onload 处理函数,并且在内部维持一个对原始函数的引用,这样无论多少次被覆盖,你的逻辑都能确保在最后执行。下面是一个改进的版本,它使用了一个循环检查机制来持续监控并确保自己的加载逻辑处于链的末端:

(function manageOnLoad() {
    var originalOnLoad = window.onload;
    
    window.onload = function() {
        var myLoadLogic = function() {
            console.log("新添加的加载完成逻辑,确保最后执行");
        };

        if (typeof originalOnLoad === 'function') {
            originalOnLoad(); // 执行原有的 onload 函数
        }

        // 重新应用这个机制,检查是否有新的 onload 被设置,如果有,则再次确保我们的逻辑在最后
        manageOnLoad();

        // 确保我的逻辑在最后执行
        myLoadLogic();
    };
})();

这段代码的工作原理是:

  1. 首先保存当前的 window.onload 处理函数到 originalOnLoad 变量中。
  2. 重新定义 window.onload,在这个新的处理函数内部,首先检查并调用之前保存的原始 onload 函数(如果存在)。
  3. 再次调用 manageOnLoad 函数,这是一个递归调用的技巧,用于不断检查和调整,确保任何后续对 window.onload 的赋值都能被捕捉并处理,保持你的逻辑在链条的最后。
  4. 最后执行你想要添加的加载完成逻辑。

通过这种方式,你可以最大限度地确保自己的逻辑始终在所有其他 onload 处理函数之后执行,不论其他脚本如何尝试覆盖 window.onload。不过,需要注意的是,这样的技术可能会引入性能问题,尤其是如果页面上有大量脚本都试图控制 onload 事件的话。因此,最佳实践是尽量减少直接修改全局事件处理程序的做法,转而采用更为现代和模块化的 JavaScript 编程模式,比如使用事件监听器或者库如 jQuery 的 .ready() 方法,以及 ES6 的 Promiseasync/await 来管理异步加载逻辑。