ES Modules顶层this为undefined,因其自动启用严格模式且顶层非函数调用上下文,故不绑定全局对象,也不支持通过this挂载属性,必须显式export。

ES Modules(ESM)内部顶层的 this 是 undefined,这不是 bug,而是语言规范的明确设计——它由两个不可分割的机制共同决定:模块自动启用严格模式 + 模块顶层作用域不绑定全局对象。
模块默认运行在严格模式下
所有 ES 模块(.mjs 文件、type="module" 脚本、或任何含 import/export 的文件)都会强制进入严格模式,无需写 "use strict"。而在严格模式中,**普通函数调用时顶层 this 不再被自动绑定到全局对象**,而是保持为 undefined。
- 对比传统脚本:
<script src="script.js"></script>中顶层this是window(浏览器)或global(Node.js) - 而
<script type="module" src="mod.mjs"></script>中console.log(this)输出就是undefined
模块顶层不是“函数调用上下文”
ES 规范将模块视为一个独立的、静态的执行单元,其顶层代码不属于任何函数调用。因此,不存在“谁调用了它”这一前提,也就没有可推导的 this 值。它既不是构造调用(new)、也不是方法调用(obj.fn()),更不是箭头函数——它就是纯粹的模块作用域,this 被显式定义为 undefined。
- 你不能通过
this.utils = {...}在模块顶层挂载工具,因为this不可写 - 试图读取
this.someProp会直接抛出TypeError(严格模式下对undefined取属性)
与 CommonJS 的本质区别
CommonJS 模块(如 Node.js 的 .cjs)顶层 this 指向 module.exports,所以可以写 this.foo = 1 相当于 module.exports.foo = 1。但 ESM 没有这种隐式导出载体——它只认 export 和 import 语句,this 不参与模块系统,自然无意义。
- ESM 中想暴露内容,必须显式
export const xxx = ... - 若需访问全局对象,请用
window(仅浏览器)、globalThis(推荐,跨环境)
这带来什么实际影响
这个设计消除了脚本时代常见的隐式全局污染,提升了模块的封闭性与可预测性。但它也意味着:旧项目中依赖 this 挂载工具或配置的写法,在迁移到 ESM 时必须重构。
- 不要写:
this.api = new ApiClient(); - 应该写:
export const api = new ApiClient();或export function createApi() { return new ApiClient(); } - 类方法、工厂函数、常量对象等显式导出方式才是 ESM 的正确姿势











