前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库ESM 顶层 this 为 undefined
JavaScript模块与语言机制

ES 模块顶层的 this 是什么?为什么和普通脚本不一样?

ES 模块顶层的 this 是 undefined;浏览器普通 <script> 标签里则是 window。Node.js 的 CommonJS 文件另有包装函数,顶层 this 指向 module.exports。需要跨环境访问全局对象时用 globalThis。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#模块与语言机制
先看核心答案读代码示例
理解线索

模块与脚本的顶层 `this`

  1. ES 模块顶层 this 是 undefined,模块有独立作用域
  2. 普通脚本顶层 this 是全局对象,var 声明的变量也挂在上面
  3. globalThis在任何环境里都能拿到全局对象,是跨环境的统一写法

这条规则和严格模式函数里 this 为 undefined 是两回事。函数里是因为没有调用者,模块顶层是因为模块本身不属于全局。

核心回答

先记住这个答案

ES 模块(<script type="module"> 或 .mjs 文件)的顶层 this 是 undefined。普通脚本(<script> 不加 type="module")的顶层 this 是全局对象。原因是模块有自己独立的作用域,模块里的代码不会挂到全局对象上,this 自然没有理由指向全局。如果确实需要访问全局对象,用 globalThis。

  • 模块顶层 this 是 undefined
  • 普通脚本顶层 this 是 globalThis
  • 要访问全局对象,用 globalThis
  • 模块顶层 this 不指向全局对象

脚本和模块的对比

在浏览器里,普通 <script> 标签执行时,顶层 this 就是 window,用 var 声明的变量也会变成 window 的属性。模块则完全独立:既不会往 window 上挂东西,顶层 this 也不指向 window。

判断当前代码是否运行在模块里HTML
<script>
  console.log(this === window) // true:经典脚本顶层
</script>
<script type="module">
  console.log(this === undefined) // true:模块顶层
</script>

Node.js 里也一样:.mjs 文件或 package.json 设了 "type": "module" 的 .js 文件里,顶层 this 是 undefined。CommonJS 的 .cjs 文件比较特殊,顶层 this 指向 module.exports,不是 undefined 也不是 globalThis,这是 Node.js 把文件包在函数里执行导致的。

为什么模块要这样设计

普通脚本的全局 this 是 JavaScript 早期的遗留设计。那时候没有模块系统,所有代码共享全局作用域,this 指向全局对象是自然的选择。

模块系统的目标是让每个文件有自己的作用域,互不污染。如果模块顶层的 this 还是全局对象,就等于给了一条绕过模块化的路——你可以用 this.x = 1 往全局上挂东西,这和模块化的初衷矛盾。所以规范直接让模块顶层的 this 为 undefined,彻底切断这条路。

这也意味着 ES 模块默认就是严格模式。和函数里 this 为 undefined 的道理相通:不做自动绑定,减少意外行为。

老库迁移时的常见问题

有些老库会在顶层用 this 来判断运行环境或注册全局变量。比如写类似 (this || window).MyLib = { ... } 的代码,指望 this 在浏览器里是 window。当你用 <script type="module"> 加载这个库时,this 变成 undefined,整个表达式就变成了 (undefined || window).MyLib。如果在 Node.js 或 Web Worker 这类没有 window 的环境里,代码就直接报错了。

用 globalThis 统一访问全局对象JavaScript
// 正确的跨环境写法
var global = globalThis
console.log(typeof global)
查看输出与解释
object

globalThis 是 ES2020 引入的标准属性,在浏览器、Node.js、Web Worker 里都能拿到全局对象。迁移老代码时把 this 或 self 替换成 globalThis 就行。

回答前,多想一步

容易答错的地方

把模块顶层 this 和函数里 this 混为一谈
严格模式函数里 this 为 undefined 是因为没有调用者,这是 this 绑定规则的一部分。模块顶层 this 为 undefined 是规范直接规定的,和调用方式无关。两者结果一样,但原因不同。面试时要分开说。
以为 CommonJS 模块的 this 也是 undefined
CommonJS 的 .cjs 文件里,顶层 this 指向 module.exports,不是 undefined 也不是 globalThis。这是 Node.js 把文件包在 (function(exports, require, module, __filename, __dirname) { ... }) 里执行导致的特殊行为。
试着用自己的话回答

面试官还会怎么问?

globalThis 出来之前怎么跨环境拿全局对象?

常见写法是 typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : self。这种三元判断又长又容易漏场景,globalThis 的出现一劳永逸解决了这个问题。

为什么 ES 模块默认是严格模式?

模块化的目标是隔离和可预测。严格模式禁止了静默错误、全局 this 绑定、with 语句等容易引起混乱的特性,和模块化的目标一致,所以规范直接把两者捆绑在一起。

<script type="module"> 和普通 <script> 还有哪些区别?

模块默认 defer(不阻塞页面渲染),有独立作用域(var 不挂全局),支持 import 和 export,且同一模块只执行一次(多次引用不重复执行)。普通脚本加载就执行,引入几次就执行几次。

从一道题,走向一组知识

把知识连起来

模块与语言机制

为什么 import 声明只能写在模块顶层?

模块除了 this 不同,import 也必须写在顶层,两者都是模块和脚本的关键区别。

模块与语言机制

顶层 await 会让依赖这个模块的代码等多久?

顶层 await 也只能在模块里用,和顶层 this 为 undefined 一样是模块独有的特性。

参考资料

  • JavaScript:模块与语言机制参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 脚本和模块的对比
  3. 为什么模块要这样设计
  4. 老库迁移时的常见问题
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑