javascript函数声明在编译阶段被整体提升并登记,而执行逻辑延至调用时;函数表达式不参与提升,仅变量名被声明,赋值在执行阶段完成。

JavaScript 函数声明的编译过程不是“写完就执行”,而是分阶段完成的。核心在于:函数声明在**编译阶段就被识别、登记并提升**,但真正执行逻辑要等到调用时才发生。
函数声明在编译阶段被整体提升
当 JS 引擎(如 V8)开始处理代码时,会在正式执行前进入编译阶段。此时它会扫描整个作用域,专门收集两类关键信息:
-
函数声明(
function foo() {...})会被完整记录:函数名、参数列表、函数体全部纳入词法环境; -
变量声明(
var bar;)只登记名称,初始值设为undefined; - 函数声明优先级高于同名的
var声明——如果两者重名,函数声明会覆盖var的undefined占位。
全局与函数内部的编译对象不同
编译阶段会为不同作用域创建对应的绑定容器:
- 全局作用域 → 创建 全局对象(GO),比如浏览器中是
window;函数声明直接挂载为 GO 的属性,值为函数体; - 函数执行前 → 创建 活动对象(AO);形参、
var变量、内部函数声明都作为 AO 属性,初始值统一为undefined; - 接着将实参值赋给对应形参,并把函数声明的值(函数体)填入 AO 中同名属性。
函数表达式不参与编译阶段提升
注意区分函数声明和函数表达式:
-
function greet() {}是声明,编译阶段就登记并提升; -
const greet = function() {}或var greet = function() {}是表达式,右侧函数体在编译阶段不处理,只登记变量名greet; - 赋值动作(
=)属于执行阶段,所以greet在执行到该行之前仍是undefined(var)或未初始化(let/const)。
编译完成后才进入执行阶段
编译只是准备上下文结构,不运行任何语句:
- A0 或 GO 中的函数体只是“可调用对象”,还没被执行;
- 只有代码执行到
greet()这类调用语句时,才会创建新的执行上下文、压栈、绑定参数、逐行解释函数体; - 此时才能访问闭包、修改外部变量、触发递归等真实行为。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











