esm模块变量默认不挂载到window上,这是设计使然;其顶层const/let/function声明仅在模块词法作用域内有效,不会污染全局,需通过export/import显式共享,手动赋值window会破坏封装。

ESM(ECMAScript Module)模块中的变量默认不会挂载到 window 对象上,这不是“bug”,而是设计使然——它保障了模块作用域的隔离性和运行时的安全性。
模块作用域天然独立于全局作用域
ESM 模块拥有自己的词法作用域,所有用 const、let、function 或 class 声明的顶层绑定,都只在该模块内部有效。它们不会自动成为 window 的属性,哪怕是在浏览器环境中。
这和传统 <script></script> 标签(非 module)的行为完全不同:后者执行在全局作用域,声明会直接污染 window。
-
<script src="a.js"></script>→ 全局执行 →var x = 1等价于window.x = 1 -
<script type="module" src="b.js"></script>→ 模块作用域 →const x = 1不影响window
不能通过 window 访问,不是“丢失”,而是“未暴露”
模块变量没有被销毁,也没有被隐藏,只是没有被显式挂载。你可以验证这一点:
- 在模块内
console.log(this === window)→ 输出false(ESM 中顶层this是undefined) - 在控制台输入
window.x→undefined,但模块内x仍可正常使用 - 如果模块导出
export const x = 1,其他模块可通过import {x}获取,而非window.x
刻意挂载需显式赋值,且不推荐
如果你确实需要让某个模块变量变成全局可访问(例如调试或与旧脚本交互),必须手动赋值:
-
window.myModuleVar = x;—— 这样才可见 - 但这种做法破坏封装,易引发命名冲突,也违背 ESM 设计初衷
- 更合理的替代是:用
export+import显式共享,或通过一个统一的全局注册函数管理
和 script 标签加载时机无关,本质是执行上下文不同
有人误以为“变量不可见是因为 JS 加载太早”,其实不是。即使把 ESM 脚本放在











