全局执行上下文在脚本加载完成时立即创建,受HTML中<script>标签顺序影响,var/function声明提升并共享,let/const受暂时性死区限制;动态插入脚本创建独立上下文;模块脚本不共享全局环境,需通过window显式挂载或ES模块规范管理变量。

全局执行上下文在脚本加载完成的第一时间就被创建,它不等 DOM 就绪、也不等代码执行,而是由 JS 引擎在解析阶段主动构建。这个上下文是整个页面生命周期中唯一一次创建的,但它的初始化内容,直接受 HTML 中 <script> 标签的加载顺序影响。
多个脚本按 HTML 顺序参与同一轮变量提升
所有普通(非模块)脚本,无论内联还是外链,只要出现在 HTML 中,就会按书写顺序依次被引擎读取,并在同一个全局执行上下文的创建阶段中完成变量和函数声明的“注册”:
-
var声明会被提升并初始化为undefined; -
function声明会被提升并赋值为函数对象; -
let/const声明也会被注册进词法环境,但处于“暂时性死区”,访问会报错; - 每个脚本的声明部分(不是赋值语句)都叠加到同一套变量环境和词法环境中。
例如:
<script>console.log(a); var a = 1;</script> <script>console.log(a);</script>
第一段输出 undefined,第二段输出 1——因为 var a 在第一脚本中已提升并挂载,第二脚本执行时 a 已存在且已被赋值。
动态插入脚本触发独立上下文
用 document.createElement('script') 插入的脚本,会启动新的加载流程,并创建一个新的全局执行上下文(注意:不是嵌套,而是隔离)。
- 它不会继承前一个上下文中的
let/const变量; -
var和function声明仍会挂载到window,但执行时机晚于静态脚本; - 若该动态脚本含
type="module",则完全不共享全局对象,也不会挂载任何顶层声明。
模块脚本(type="module")不参与全局上下文共享
带 type="module" 的脚本:
- 不向
window挂载变量或函数; - 不参与传统全局上下文的变量提升流程;
- 拥有独立的模块级词法作用域,
this为undefined; - 执行顺序由
import依赖图决定,而非 HTML 位置。
这意味着:
<script>console.log(shared);</script> <script type="module">const shared = 'from module';</script>
第一段会报错 ReferenceError: shared is not defined——模块内的 shared 对全局不可见。
全局变量共享的本质是 window 属性映射
在浏览器中,普通脚本里用 var x = 1 声明的变量,等价于 window.x = 1;而 let x = 1 或 const x = 1 声明的变量,虽可通过 window.x 访问(引擎自动代理),但它不是 window 的可枚举属性,也不会被 for...in 遍历。
因此跨文件共享变量最直接的方式仍是:
- 统一用
window.myApp = window.myApp || {}建立命名空间; - 避免裸变量名(如
config = {...}),防止被覆盖或污染; - 优先用 ES 模块
export/import替代隐式全局,尤其在现代项目中。
不复杂但容易忽略

















