You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

动态添加脚本onload回调找不到全局函数,求除setTimeout外的解决方案

问题描述

步骤1:加载外部脚本,将onload设置为调用window.initControllers:

(function(document, tag) {
    var script = document.createElement(tag),
        el = document.getElementsByTagName(tag)[0];
    script.src = 'https://unpkg.com/@hotwired/stimulus@3.1.0/dist/stimulus.umd.js';
    script.onload = function () {
        window.initControllers();
    };

    el.parentNode.insertBefore(script, el);
}(document, 'script'));

步骤2:在另一个动态添加的脚本中定义window.initControllers函数:

(function(document, tag) {
    var script = document.createElement(tag),
        el = document.getElementsByTagName(tag)[0];
    script.type = 'module';
    var contents = "var initControllers = function () { console.log('initControllers'); };",
    contents += 'window.initControllers = initControllers;';

    script.appendChild(document.createTextNode(contents));

    el.parentNode.insertBefore(script, el);
}(document, 'script'));

报错情况

部分页面刷新时出现以下错误:

Uncaught TypeError: window.initControllers is not a function.

提问

除了使用setTimeout相关的复杂方法外,有哪些方案可以确保步骤1的脚本加载时,window.initControllers已被定义?


解决方案
  • 调整脚本加载顺序
    将步骤2的脚本插入逻辑放在步骤1之前执行。当前两个脚本都是异步插入,且模块脚本(type="module")默认延迟执行,可能导致步骤1的外部脚本先加载完成,此时initControllers还未挂载到window。先插入定义函数的脚本,再加载外部脚本,可保证函数先被定义。

  • 移除模块脚本类型
    模块脚本会在所有同步脚本执行完毕后才运行,而非模块脚本会立即执行。删除步骤2中的script.type = 'module';,插入的脚本会同步执行,window.initControllers会被立即定义,避免未定义问题。

  • 将外部脚本加载逻辑嵌套到函数定义脚本中
    在步骤2的脚本内容里,定义完initControllers后再动态加载步骤1的外部脚本,从根源上保证依赖顺序:

    (function(document, tag) {
        var script = document.createElement(tag),
            el = document.getElementsByTagName(tag)[0];
        script.type = 'module';
        var contents = `
            var initControllers = function () { console.log('initControllers'); };
            window.initControllers = initControllers;
            // 加载外部脚本
            const stimScript = document.createElement('script');
            stimScript.src = 'https://unpkg.com/@hotwired/stimulus@3.1.0/dist/stimulus.umd.js';
            stimScript.onload = () => window.initControllers();
            document.head.appendChild(stimScript);
        `;
        script.appendChild(document.createTextNode(contents));
        el.parentNode.insertBefore(script, el);
    }(document, 'script'));
    
  • 使用自定义事件实现依赖通知
    在步骤2中定义完函数后触发自定义事件,步骤1的外部脚本加载完成后,先检查函数是否存在,不存在则监听事件,确保函数就绪后再调用:
    步骤2修改后代码:

    (function(document, tag) {
        var script = document.createElement(tag),
            el = document.getElementsByTagName(tag)[0];
        script.type = 'module';
        var contents = `
            var initControllers = function () { console.log('initControllers'); };
            window.initControllers = initControllers;
            // 触发就绪事件
            window.dispatchEvent(new CustomEvent('initControllersReady'));
        `;
        script.appendChild(document.createTextNode(contents));
        el.parentNode.insertBefore(script, el);
    }(document, 'script'));
    

    步骤1修改后代码:

    (function(document, tag) {
        var script = document.createElement(tag),
            el = document.getElementsByTagName(tag)[0];
        script.src = 'https://unpkg.com/@hotwired/stimulus@3.1.0/dist/stimulus.umd.js';
        script.onload = function () {
            if (typeof window.initControllers === 'function') {
                window.initControllers();
            } else {
                window.addEventListener('initControllersReady', () => {
                    window.initControllers();
                }, { once: true });
            }
        };
        el.parentNode.insertBefore(script, el);
    }(document, 'script'));
    

内容的提问来源于stack exchange,提问作者gremo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 14:57:29