函数声明在编译阶段被提升并绑定函数对象,可提前调用;函数表达式在执行时创建函数对象并赋值,必须先定义后调用;命名函数表达式的内部名仅限函数体内使用,箭头函数同属表达式,不提升且无this。

函数声明和函数表达式在内存中都作为函数对象(Function Object)被创建,本质上都是引用类型,存储在堆(heap)中;但它们的**创建时机、绑定方式和作用域可见性**直接影响了内存中“变量名”与“函数体”的关联逻辑,进而造成行为差异。
函数声明:先绑定,再执行
函数声明在**编译阶段(进入执行上下文时)就被解析并提升(hoisted)**,引擎会为函数名在当前作用域的词法环境(Lexical Environment)中预先创建一个绑定,并直接指向内存中已构建好的函数对象。这个过程发生在代码执行前,所以函数名在作用域内始终可访问、可调用。
- 函数名是必须的,且在作用域中作为不可配置、不可删除的绑定存在
- 函数对象本身在内存中只创建一次,且与函数名强绑定
- 即使写在文件末尾,也能在顶部被调用——因为绑定已完成,只是还没执行到函数体
函数表达式:运行时赋值,按序求值
函数表达式本质是一个赋值操作(如 const fn = function() {}),它的右侧函数字面量在**执行到该语句时才被创建为函数对象**,然后作为值赋给左侧变量。变量本身遵循 let/const 的初始化规则:声明被提升,但初始化(即赋值)不提升。
- 变量名在作用域中存在,但初始值为 undefined(var)或处于“暂时性死区”(let/const)
- 函数对象在赋值那一刻才分配内存,且与变量名通过普通引用关系关联
- 调用必须发生在赋值之后,否则访问的是未初始化的变量或 undefined
命名函数表达式的特殊处理
像 const f = function named() {} 这样的命名函数表达式,其内部名称 named 仅在函数体内可用(用于递归或调试栈追踪),不会在外部作用域中创建绑定。也就是说:named 不占用额外作用域槽位,也不在内存中多存一份函数对象——它只是函数对象自身的 name 属性,不影响存储结构。
- f.name === 'named',但 typeof named === 'undefined'
- 内存中仍只有一个函数对象,只是多了个内部标识名,便于开发者工具识别
箭头函数与函数表达式同属一类
箭头函数(const fn = () => {})属于函数表达式的语法糖,同样不被提升,遵循相同的内存绑定逻辑:变量声明提升,函数对象创建和赋值发生在运行时。它没有自己的 this/arguments,但存储机制与普通函数表达式一致——都是执行时动态构造、按需绑定。
- 不会创建独立的作用域绑定,依赖外层词法环境
- 在内存中也是 Function 实例,只是内部实现略有优化(如无 prototype)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











