You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript解释器是单次解释执行代码还是分两次执行?

JavaScript 代码执行流程的准确说明

首先直接给结论:现代JavaScript引擎执行代码不是单遍读取边读边跑完全流程,但也不是简单的“读完整文件→全量分配内存→把声明挪到栈顶→再跑第二遍”,实际执行是按「执行上下文」为单位分两个标准阶段推进的,具体逻辑如下:

  • 第一阶段:执行上下文创建阶段(对应你提到的第一次遍历逻辑)
    当JS引擎进入一段可执行代码(全局代码、被调用的函数代码、eval代码)时,不会立刻逐行跑逻辑,会先完成当前上下文的初始化工作:
    • 先做词法、语法分析生成抽象语法树(AST),同时扫描当前作用域内的所有声明:var声明的变量会被初始化为undefined,function声明的函数会直接绑定完整的函数体引用,let/const/class声明的标识符会被登记但保持未初始化状态(也就是常说的暂时性死区);
    • 这里要明确:不存在物理上把变量声明移动到代码块顶部的操作,大家常说的“变量提升”只是这个声明登记步骤带来的感知效果,引擎不会修改你的源码位置;
    • 内存的初始分配在这个阶段完成,但注意:对象、数组这类引用类型的实际堆内存占用,是执行到对应赋值语句时才会分配,不是这个阶段一次性把所有内存都分完。
  • 第二阶段:逐行执行阶段(对应你提到的第二次遍历逻辑)
    上下文创建完成后,引擎才会开始从上到下逐行执行代码,完成变量赋值、表达式运算、函数调用等操作:
    • 执行到var变量的赋值语句时,才会把之前初始化为undefined的变量修改为对应的值;
    • 执行到let/const/class的赋值语句时,才会完成对应标识符的初始化,解除暂时性死区,在这之前访问对应标识符会直接抛引用错误;
    • 执行到函数调用语句时,会立刻为被调用的函数创建新的独立执行上下文,重新走一遍「创建阶段→执行阶段」的完整流程——也就是说,函数内部的代码在函数没被调用前,不会进入创建阶段,更不会被提前执行,不存在“首次加载就读完整个文件所有代码做两次全局遍历”的情况。

两个常见误区需要纠正:

  1. 不要把全局代码的两阶段执行套用到整个文件的所有代码:未被调用的函数内部代码,在首次脚本加载时甚至不会被完整解析,更不会提前做声明登记;
  2. 不要把提升理解为源码位置变动:所有提升效果都是声明登记机制带来的,和代码物理位置移动没有关系。

你可以用下面这段测试代码验证两阶段逻辑,结果完全符合上述机制,和“单遍边读边执行”的假设矛盾:

// 第一行就访问var声明的a,不会报错,输出undefined
console.log(a)
// 访问let声明的b,直接抛ReferenceError,因为b在暂时性死区
console.log(b)

var a = 1
let b = 2

// 函数声明在定义前就可以正常调用,输出"function run"
foo()
function foo() {
  console.log('function run')
}

内容的提问来源于stack exchange,提问作者ColdHands

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 13:21:31