函数声明提升是执行上下文创建时vo初始化阶段完成的,函数对象直接赋值,var变量仅声明为undefined;函数表达式不提升。

函数声明提升不是“代码被挪到前面”这种表面理解,而是 JavaScript 引擎在进入执行阶段前,通过创建执行上下文时的预解析(hoisting phase)完成的——它发生在堆栈准备阶段,而非运行时动态移动代码。
执行上下文创建时的变量对象初始化
当 JS 引擎开始执行一段可执行代码(如全局脚本或函数调用),会先为它创建一个执行上下文(EC)。这个过程包含两个关键动作:
- 在变量对象(VO)中,为所有
function声明创建属性,值直接指向该函数的定义(即函数对象本身); - 同时为所有
var声明创建属性,但初始值统一设为undefined; - 这个 VO 是执行上下文的一部分,存在于内存中的执行环境栈(ECS)里,不是运行时才生成的对象。
为什么函数能“提前调用”,而 var 变量不能?
根本区别在于初始化时机和赋值行为:
- 函数声明:在 VO 初始化阶段,就完成了“声明 + 赋值”两步,函数对象已就位;
- var 变量:VO 中只完成“声明”,赋值语句仍保留在原位置,要等到执行阶段才真正执行;
- 所以
fn()在第一行就能成功,是因为它访问的是 VO 中早已存在的完整函数对象;而console.log(a)输出undefined,是因为 VO 中的a确实存在,只是还没被赋值。
执行上下文栈中的实际表现
以如下代码为例:
console.log(fn); // function fn() { ... }
fn();
function fn() { console.log('ok'); }
引擎实际执行流程是:
- 启动全局执行上下文(ECG),压入 ECS 栈底;
- 进入预解析阶段:在 ECG 的 VO(G) 中添加属性
fn,值为函数对象; - 进入执行阶段:逐行解释字节码,
console.log(fn)从 VO(G) 中取值,自然拿到函数定义; - 后续调用
fn()时,新建函数执行上下文,压入 ECS 栈顶,执行其内部逻辑。
注意:函数表达式不享受同等待遇
如果写成 var fn = function() {...},本质是 var 声明 + 赋值操作:
- VO 中仅创建
fn: undefined; - 赋值语句仍按顺序执行,因此
fn()在声明前调用会报TypeError: fn is not a function; - 这说明“提升”只对
function关键字声明有效,与语法结构强相关,不是凭名字识别的。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











