725 字
4 分钟
JavaScript 变量提升&函数提升
.png)
核心定义
JavaScript 引擎在代码执行前会进行编译阶段,此过程将:
- 函数声明整体提升到作用域顶部
- 变量声明(
var
)仅提升声明(不提升赋值),初始化为undefined
- 赋值操作和非声明代码留在原地 这种机制称为提升(Hoisting),
let
/const
有暂时性死区(TDZ),在声明前访问会报错。
工作原理
- 编译阶段:
- 扫描作用域内的函数声明和
var
变量 - 函数声明优先提升(创建函数对象)
var
变量初始化为undefined
- 扫描作用域内的函数声明和
- 执行阶段:
- 按顺序执行代码
- 遇到赋值操作时才更新变量值
let
/const
在执行到声明语句时才初始化
flowchart TD A[编译阶段] --> B[提升函数声明(整体)] A --> C[提升var声明(初始化为undefined)] D[执行阶段] --> E[执行赋值操作] D --> F[执行函数调用] D --> G[let/const初始化(TDZ结束)]
关键点
- 提升优先级:函数声明 > 变量声明(同名时函数覆盖变量)
- 函数表达式不提升(如
var foo = function(){}
) - 块级作用域:ES6 的
let
/const
不提升(但有 TDZ) - 重复声明:
var
允许重复声明let
/const
禁止重复声明
- 严格模式:
'use strict'
禁止意外创建全局变量
常见误区
- 误解函数表达式:认为
var foo = function(){}
会整体提升 - 混淆函数与变量:同名时误以为变量声明覆盖函数
- 忽略 TDZ:在
let
声明前访问变量(报错 vsundefined
) - 块级函数声明:ES5 非严格模式中块内函数声明行为不一致
- 提升范围误判:认为
if
块内的var
会限制在块作用域(实际函数级)
应用场景
场景 | 示例 | 提升行为 |
---|---|---|
函数调用在声明前 | foo(); function foo() {...} | 正常执行(函数整体提升) |
var 提前访问 | console.log(a); var a = 1; | 输出 undefined (声明提升) |
let 暂时性死区 | console.log(b); let b = 2; | 报错 ReferenceError |
函数表达式 | bar(); var bar = () => {...} | 报错 bar is not a function |
同名函数与变量 | console.log(c); var c; function c(){} | 输出 ƒ c(){} (函数优先覆盖) |
重复声明 | var d = 1; var d; console.log(d); | 输出 1 (忽略重复声明) |
关联知识
- 执行上下文:
- 创建阶段(提升发生)
- 执行阶段(代码运行)
- 作用域链:词法作用域决定变量访问范围
- 闭包:函数保留其声明时的作用域链
- 严格模式:
- 禁用
with
- 禁止意外全局变量
- 对
eval
限制作用域
- 禁用
- 模块作用域:ES6 模块自动启用严格模式
- 事件循环:提升在同步代码执行前完成
💡 黄金法则:
- 始终使用
const
>let
>var
- 函数优先使用声明式(
function foo()
) - 避免在块内声明函数(用函数表达式替代) 通过
eslint
规则(如no-use-before-define
)捕获提升相关问题!