函数声明提升本质是执行上下文创建时变量对象(vo)的初始化行为:函数声明在vo中直接赋值,var声明仅初始化为undefined;函数表达式仅变量名提升,函数体不提升;同名时函数声明优先,但后续赋值会覆盖。

函数声明提升不是代码被“挪到前面”,而是 JavaScript 引擎在创建执行上下文时,对变量对象(VO)进行初始化的结果。这个过程发生在代码真正执行之前,属于编译阶段的预解析行为,与运行时无关。
执行上下文创建时的 VO 初始化
每当 JS 引擎开始执行一段可执行代码(如全局脚本或函数调用),会先为其创建一个执行上下文(EC)。这个过程包含两个关键动作:
- 在变量对象(VO)中,为所有 function 声明 创建属性,并直接将对应函数对象赋值给该属性;
- 同时为所有 var 声明 创建属性,但初始值统一设为 undefined;
- 这个 VO 是执行上下文的一部分,存在于内存中的执行环境栈(ECS)里,不是运行时动态生成的对象。
为什么函数能提前调用,而 var 变量不能?
根本区别在于初始化时机和赋值行为:
- 函数声明:在 VO 初始化阶段就完成了“声明 + 赋值”两步,函数对象已就位;
- var 变量:VO 中只完成“声明”,赋值语句仍保留在原位置,要等到执行阶段才真正执行;
- 因此
fn()在第一行就能成功,是因为它访问的是 VO 中早已存在的完整函数对象;而console.log(a)输出 undefined,是因为 VO 中的a确实存在,只是还没被赋值。
函数表达式不享受提升待遇
如果写成 var fn = function() {},本质是 var 声明 + 赋值操作:
- VO 中仅创建
fn: undefined; - 赋值语句仍按顺序执行,因此在声明前调用会报 TypeError(因为此时
fn是undefined,无法调用); - 这和
function fn() {}的完整提升有本质区别。
提升优先级:函数声明 > var 声明
当函数名和 var 变量名冲突时,引擎会按固定顺序处理:
- 函数声明先被登记进 VO,函数对象直接就位;
- 随后 var 声明也会登记,但不会覆盖已存在的函数属性;
- 只有后续的赋值语句(如
fn = 123)才会真正改变 VO 中该属性的值。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











