← 返回题目列表

什么是变量提升?JavaScript 执行上下文是如何创建的?

高频 中等 第 8 / 29 题 更新于 2026/07/28
JavaScript变量提升执行上下文TDZ作用域

简化版

变量提升是执行上下文创建阶段对声明进行预处理的结果。var 会被初始化为 undefined,函数声明会整体可用,let/const 也会被记录但处于暂时性死区,在初始化前访问会报错。

详细版

  • 执行上下文通常分为创建阶段和执行阶段。
  • var 声明会绑定到函数或全局作用域,并在创建阶段初始化为 undefined。
  • 函数声明会在创建阶段完成绑定,因此可以先调用后声明。
  • let/const/class 有提升但未初始化,进入 TDZ 后在声明前访问会抛 ReferenceError。
  • 面试时要避免说“let/const 不提升”,更准确是“提升但处于暂时性死区”。

完整版教学

记忆钩子:提升不是代码真的搬家,而是执行前先建了一张变量登记表。

一、这题真正考什么

记忆钩子:提升不是代码真的搬家,而是执行前先建了一张变量登记表。

面试官想看的不是你能不能背一个术语,而是能不能解释 JavaScript 为什么会出现这些看似反直觉的结果。答题时要从执行模型、规范规则和工程边界三个角度展开。

二、底层机制怎么理解

JavaScript 运行一段代码前,会先创建执行上下文,建立变量环境和词法环境。声明被扫描并登记到对应环境中,随后代码才逐行执行。var 的特殊之处在于它创建时就被赋值为 undefined,而 let/const 在声明语句执行前不可访问。

JavaScript 高频题经常围绕“运行时如何决定结果”展开。只要能把创建阶段、调用方式、查找链路、转换规则或内存可达性说清楚,就能把题目从记忆题讲成原理题。

三、带数字或代码的小推演

代码 console.log(a); var a = 1; 会输出 undefined,因为创建阶段已有 a。代码 console.log(b); let b = 1; 会报错,因为 b 虽然已在词法环境中登记,但还没完成初始化。

下面把核心规则压成几行,复习时可以按顺序手推一遍:

创建阶段:收集声明 -> 建立环境记录 -> 初始化 var/function
执行阶段:逐行运行 -> 执行赋值 -> 进入/离开作用域
TDZ = 词法绑定存在,但初始化之前不可访问的时间段

四、执行流程怎么走

可以把这道题放到下面的运行流程里看:

进入全局或函数
   |
创建执行上下文
   |
扫描 var/function/let/const/class
   |
初始化 var 和函数声明
   |
开始逐行执行代码
   |
执行到 let/const 声明时完成初始化

流程图的价值是把“现象”还原成“步骤”。面试追问通常就是换一段代码让你预测输出,按流程推比靠直觉猜更稳。

五、对比表格

声明方式作用域创建阶段表现
var函数/全局作用域初始化为 undefined
function 声明函数/块级规则较复杂函数体可用
let块级作用域TDZ,声明前不可访问
const块级作用域TDZ,且必须初始化
class块级作用域TDZ,声明前不可访问

对比题要主动说适用场景和边界。JavaScript 的很多机制都不是“好或坏”,而是历史兼容、运行时灵活性和工程可维护性之间的折中。

六、工程实践和排查思路

  • 不要把“提升”理解成源码被物理移动,否则很难解释 TDZ。
  • 在同一作用域混用 var 和 let,容易出现重复声明、覆盖全局和循环闭包问题。
  • 遇到输出预测题时,不要只报结果,要按创建、查找、调用、转换、执行顺序解释。
  • 生产代码要优先选择可读性强、边界清晰的写法,把语言怪异点限制在工具函数或明确封装里。

七、常见误区与追问

  • 误区:let 和 const 完全不会提升。 要补上规范规则、执行阶段和反例,避免用一句经验话术盖过真正原因。
  • 误区:变量提升就是代码被移动到了顶部。 要补上规范规则、执行阶段和反例,避免用一句经验话术盖过真正原因。
  • 误区:函数表达式和函数声明提升效果一样。 要补上规范规则、执行阶段和反例,避免用一句经验话术盖过真正原因。
  • 追问:为什么 typeof 未声明变量和 TDZ 变量表现不同? 先给规则,再用一小段代码或内存/调用示意解释为什么。
  • 追问:for 循环里的 let 为什么能解决闭包问题? 先给规则,再用一小段代码或内存/调用示意解释为什么。
  • 追问:全局 var 和 window 属性有什么关系? 先给规则,再用一小段代码或内存/调用示意解释为什么。

八、加强记忆

按“先建上下文,再执行代码”记。var 是先登记并给 undefined,函数声明是先登记并给函数体,let/const/class 是先登记但锁在 TDZ 里。

复习 JavaScript 基础题时,可以固定按“创建阶段、执行阶段、查找链路、调用方式、类型转换、工程边界”六步组织答案。这个框架能覆盖大多数输出题和原理题。