函数声明会被完整提升,而匿名函数表达式仅变量声明被提升、赋值和函数体不提升;var声明的表达式调用报TypeError,let/const则抛ReferenceError。

函数声明会被提升(hoisting),但匿名函数表达式不会——这是理解JavaScript执行上下文和变量生命周期的关键区别。
函数声明会完整提升
当使用 function 关键字直接定义函数(即函数声明)时,整个函数定义会在代码执行前被“提升”到当前作用域顶部,包括函数名和函数体。这意味着你可以在声明之前调用它:
console.log(add(2, 3)); // 5
function add(a, b) {
return a + b;
}
这段代码能正常运行,因为 add 函数在进入执行阶段前已被解析并绑定到作用域中。
匿名函数表达式只提升变量名,不提升赋值
当把函数写成赋值形式(如 const fn = function() {...} 或 var fn = function() {...}),本质是“变量声明 + 函数创建 + 赋值”三步。只有 变量声明部分会被提升,而函数本身和赋值操作仍保留在原位置:
立即学习“Java免费学习笔记(深入)”;
console.log(fn()); // TypeError: fn is not a function
var fn = function() {
return "hello";
};
- 使用
var:变量名fn被提升,初始值为undefined;调用时实际执行的是undefined(),报错 - 使用
let或const:变量声明仍被提升,但处于“暂时性死区”(TDZ),在声明前访问会直接抛出ReferenceError
箭头函数和命名函数表达式也遵循表达式规则
箭头函数(const fn = () => {})和命名函数表达式(const fn = function named() {})都属于函数表达式范畴,同样不会被提升函数体:
- 它们的标识符(如
fn)是否可提前访问,取决于声明方式(var/let/const) - 函数名
named在函数体内可用(用于递归等),但在外部不可访问,也不参与提升
如何避免因提升差异导致的错误
推荐采用一致、可预测的函数定义方式:
- 优先使用
const声明函数表达式,明确表达“不可重新赋值”的意图 - 需要提升行为时,用函数声明;否则统一放在调用点之后,或确保声明在使用前完成
- 现代开发中,借助 ESLint 规则(如
no-use-before-define)主动拦截提前使用 - 模块级顶层逻辑建议用 IIFE 或显式初始化函数,减少对提升机制的依赖










