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

为何模块中给window赋值函数无效?如何供非模块脚本调用?

问题

场景代码

module.mjs

console.log('module1 loaded');

window.module1_function = () => {
   console.log('module1_function executed');
   return 42;
}

index.html

<script defer type="module" src="./js/module1.mjs"></script>

<script defer>
   module1_function();
</script>

执行结果

Uncaught ReferenceError: module1_function is not defined

module1 loaded

可见调用失败,函数未定义,模块在函数调用后才加载;但在控制台执行module1_function()却能成功运行。

疑问

defer本应让内联脚本等待module.mjs执行完成后再处理,此时函数已赋值应可调用,为何报错?

需求

如何让模块中的该函数对普通脚本可用,且无需将普通脚本转为模块并导入函数?


解答

报错原因

带type="module"的脚本和普通脚本的defer执行逻辑存在差异:

  • 普通defer脚本会按HTML中的顺序,在DOM解析完成后、DOMContentLoaded事件触发前依次执行。
  • 模块脚本(即便加了defer)的执行时机是所有普通defer脚本执行完毕之后,DOMContentLoaded事件触发之前。

所以你的代码中,普通内联defer脚本先执行,此时模块脚本还未运行,window.module1_function尚未被定义,因此抛出引用错误。而控制台执行时页面已完全加载,模块脚本早已执行完毕,自然能找到该函数。

解决方案

方法1:监听DOMContentLoaded事件

将普通脚本的函数调用放在DOMContentLoaded事件回调中,模块脚本会在该事件触发前执行完毕:

<script defer type="module" src="./js/module1.mjs"></script>

<script>
   document.addEventListener('DOMContentLoaded', () => {
      module1_function();
   });
</script>

这里可去掉普通脚本的defer属性,事件回调本身会等待DOM加载完成后触发。

方法2:调整脚本顺序并移除普通脚本的defer

将普通内联脚本放在body末尾,同时移除它的defer属性。模块脚本默认延迟执行(等效于defer),会在DOM解析完成后先执行,随后执行末尾的普通脚本:

<body>
   <!-- 页面内容 -->
   <script type="module" src="./js/module1.mjs"></script>
   <script>
      module1_function();
   </script>
</body>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 16:14:53