题目 01 / 14

为什么函数中的局部变量输出 undefined?

知识点: 声明实例化

函数内的 var 声明会在赋值语句执行前创建局部绑定。

使用执行可视化器解题在下方逐步观察每个绑定、函数调用和控制台输出。

预测输出结果

预测输出结果。函数读取的是外层的 a,还是尚未赋值的局部 a?

var a = 10;
function fn() {
  console.log(a);
  var a = 20;
}
fn();
查看答案与完整解析

正确输出

undefined

为什么会这样

声明实例化阶段会把 fn 内的 var a 提升到函数体执行之前,创建一个初始值为 undefined 的局部 a 绑定。

这个局部绑定在整个函数体中遮蔽外层的 a,因此 ResolveBinding 会先找到局部 a,不会再查找外层环境。

赋值语句 var a = 20 要到下一行才执行,这时输出语句已经运行完毕,所以即使外层存在值为 10 的 a,console.log(a) 依然输出 undefined。

这道题考察什么

  • var 提升
  • 局部变量遮蔽
  • 函数作用域声明

运行环境前提

在现代 JavaScript 环境的经典脚本或模块中,结果一致。

规范参考

JavaScript 代码编辑器支持直接编辑或粘贴自定义代码
第 0 步 / 共 0 步
单步Space播放
按 → 单步 或 Space 自动播放
阶段 1: 编译预解析(变量提升与内存分配)
阶段 2: 代码执行(逐行赋值与调用求值)
单步执行原理解析

按键盘右方向键 “→” 进行单步调试,或按空格键 “Space” / 点击 “▶ 自动播放” 进行连续执行。

作用域链 (Scope Chain)
词法作用域链: 当前 AO → 外部 [[Scope]] → 全局 (GO)
暂无激活的作用域链
调用栈 (CALL STACK)执行上下文栈 (后进先出 LIFO)
后进先出 (LIFO) 栈帧
全局执行上下文 (GEC)
▶ 活动栈帧
堆内存空间 (HEAP & MEMORY SPACE)已分配对象 (GO, AO, FunctionObjects, ER)
ECMAScript 堆内存区
内存中暂无分配对象
控制台输出 (Console)
console.log(...) Stream
暂无输出产生