全局执行上下文在脚本加载完成的第一时间由js引擎解析阶段主动创建,不等待代码执行或dom就绪;多个脚本按html顺序依次参与同一轮创建(含变量提升),动态插入脚本触发独立上下文;模块脚本则启用隔离的模块级上下文,不挂载全局对象。

全局执行上下文在脚本加载完成的**第一时间**就被创建,它不等待代码执行,而是由JS引擎在解析阶段就主动构建——也就是说,加载顺序直接决定全局上下文的初始化内容和时机。
全局上下文只在脚本加载时触发一次
无论页面引入一个还是多个<script>标签,只要脚本开始被JS引擎读取(即“加载”),引擎就会立即启动全局执行上下文的创建流程。这个过程发生在任何代码执行之前,且整个页面生命周期中仅发生一次。</script>
- 多个外部脚本按HTML中出现顺序依次加载,每个脚本都会在自身加载完毕后、执行前,参与同一轮全局上下文的“创建阶段”(变量提升、函数声明挂载等)
- 内联脚本(
<script>...</script>)同样遵循所在位置的加载顺序,与外链脚本统一纳入全局上下文初始化流程 - 动态插入的脚本(如
document.createElement('script'))会触发新的加载过程,也会触发一次独立的全局上下文创建——但注意:它不是“嵌套”的全局上下文,而是另一个隔离的执行环境(例如模块脚本或带type="module"的脚本会启用不同的全局作用域规则)
加载顺序影响变量提升的可见范围
var和function声明具有提升(hoisting)特性,但它们的“注册”和“初始化”依赖于脚本的实际加载顺序。引擎按顺序扫描每个脚本块,在创建阶段将其中的var设为undefined、function赋值为函数对象,并挂载到全局对象上。
- 如果
script-A.js先加载并声明var a = 1,那么后续加载的script-B.js中可直接访问a(此时已存在,值为1) - 但如果
script-B.js在script-A.js之前加载,而它又尝试读取a,则得到undefined(因script-A.js的赋值语句尚未执行) - function声明不受此限制:即使
script-B.js先加载,只要它调用的是script-A.js中定义的函数,且该函数已在创建阶段被提升并挂载,调用就能成功
模块脚本(type="module")打破默认全局上下文共享
带type="module"的脚本不会向全局对象(如window)自动挂载变量或函数,也不参与传统意义上的“全局执行上下文”的变量提升流程。每个模块拥有自己的词法作用域,其顶层声明仅在模块内有效。
- 模块脚本仍会创建一个“模块级全局执行上下文”,但它不等于浏览器的全局上下文;
this指向undefined(非严格模式下也不绑定window) - 多个模块按
import依赖顺序执行,而非HTML书写顺序——这使得加载逻辑从“线性扫描”变为“依赖图遍历” - 普通脚本与模块脚本混合时,二者使用不同的执行上下文机制,彼此的
var/function不会互相提升或覆盖
DOMContentLoaded与全局上下文无因果关系
全局执行上下文的创建发生在HTML解析过程中,早于DOMContentLoaded事件。它不等待DOM树构建完成,也不等待CSS加载,只取决于JS引擎是否已开始处理该脚本资源。
- 同步脚本(无
async或defer)会阻塞HTML解析,其全局上下文在解析中断点处立即创建并进入执行阶段 -
defer脚本会在DOM解析完成后、DOMContentLoaded触发前按顺序执行,其全局上下文创建仍发生在脚本下载完成时,但执行被推迟 -
async脚本下载不阻塞,一旦下载完就立刻创建并执行其全局上下文,因此执行顺序不可预测











