函数声明与变量提升:定义具名函数
函数声明会完整提升到作用域顶部,所以声明前就能调用;var 变量只提升声明,初始化不提升;同名时函数声明优先于 var 声明且不会被覆盖。 函数声明提升:整个函数定义被提升到作用域顶部 函数声明(function foo() {})在 JavaScript 里会被「提升」到当前作用域顶部——这意味着
函数声明会完整提升到作用域顶部,所以声明前就能调用;var 变量只提升声明,初始化不提升;同名时函数声明优先于 var 声明且不会被覆盖。

函数声明提升:整个函数定义被提升到作用域顶部
函数声明(function foo() {})在 JavaScript 里会被「提升」到当前作用域顶部——这意味着你完全可以在声明前就调用它。不过得注意:提升的是整个函数定义,不是执行顺序提前,也不是像变量赋值那样“运行”了一遍函数。
很多人容易掉进误区,以为 console.log(foo) 在声明前会输出 undefined——实际上它输出的是函数对象本身。原因很简单:提升的是函数定义本身,不是 var 那种“声明+初始化分离”的机制。
foo()在声明前就能正常执行(例如foo(); function foo() { console.log('hi'); })typeof foo在声明前是"function",不是"undefined"- 如果同名标识符既有函数声明又有
var声明(比如var foo; function foo() {}),函数声明依然优先被提升,var声明无法覆盖它
区分函数表达式:const/let 声明的函数不享受提升
写成 const foo = function() {} 或 const foo = () => {} 属于函数表达式,受 const/let 的「暂时性死区(TDZ)」约束——访问未初始化的绑定会直接抛出 ReferenceError,不会返回 undefined。
这和 var foo = function() {} 也不一样:var 版本会提升声明(foo 变量名存在),但初始化(赋值)留在原地,所以声明前读取 foo 得到 undefined,若调用则会报 TypeError: foo is not a function。
- 函数声明:
foo()声明前可正常调用 var foo = function() {}:foo()报TypeError;console.log(foo)→undefinedconst foo = function() {}:foo()报ReferenceError(进入作用域但尚未初始化)
嵌套函数声明:提升范围仅限所在函数作用域
在函数内部使用 function bar() {} 声明,这个 bar 只在该外层函数体内被提升,对外不可见,也不会污染外层作用域。
容易踩的坑是:误以为嵌套声明会提升到全局——它不会。而且不同浏览器对块级函数声明(比如写在 if 块里)的行为曾经有分歧(ES2015 后统一为「仅在块内提升,且具有块级作用域」),主流环境现在都按标准处理。
- 在
function outer() { function inner() {} inner(); }中,inner()可以在声明前调用 - 但在
outer()外部调用inner()会报ReferenceError - 避免在非严格模式下依赖块级函数声明,严格模式下它等价于
let声明(即 TDZ 行为)
实际开发中选择函数声明还是函数表达式
函数声明适合定义逻辑清晰、需提前使用的工具函数(比如递归主入口、事件处理器骨架),尤其在模块顶层或立即执行上下文中;函数表达式则更常用于需要控制作用域、延迟初始化、或作为参数传递的场景。
真正容易被忽略的是:函数声明的提升无法跨文件、无法跨 eval 或动态 import() 边界。换句话说,如果你把 function foo() {} 放在一个异步加载的脚本里,它不会“提前”影响当前执行流中的代码。
- 模块内顶层函数声明可安全前置调用,但别指望它能在
import语句之前生效 - 使用构建工具(如 Webpack/Vite)时,提升行为仍以单个 JS 文件为单位,不跨打包产物边界
- TS 编译后是否保留提升取决于
target设置(ES5保持,ES6+一般也保持,但类型擦除不影响运行时行为)
