作用域与变量提升
1. 变量提升
JavaScript 引擎在执行代码之前,会先进行编译。在编译阶段,变量声明和函数声明会被从它们在代码中的位置移动到当前作用域的顶部。
(1) var 提升
等价于:
(2) 函数声明提升
函数声明 会整体提升(不仅仅是变量名)。
(3) 函数表达式不会提升
这里的 var foo 被提升为 undefined,调用 undefined() 当然报错。
2. 块级作用域与 TDZ (暂时性死区)
ES6 引入了 let 和 const,它们使得 {} 变为块级作用域。
(1) 什么是 TDZ ?
TDZ (Temporal Dead Zone) 是指在使用 let 或 const 声明变量之前的代码区域。
在这段区域内,如果访问该变量,会抛出 ReferenceError。
目的:强制让开发人员在声明变量后再使用它,避免像 var 那样依赖变量提升导致的不可预测行为。
(2) 为什么会有 TDZ ?
在 JavaScript 中,let/const 也是存在变量提升的。 JS 引擎在编译阶段就已经知道变量的存在(及其作用域)。
var: 声明被提升,且会被直接初始化为undefined。所以你可以在声明前访问它(值为 undefined)。let/const: 声明被提升,但不会被初始化。直到执行流运行到声明的那一行代码时,变量才会被初始化。在初始化之前,变量处于“死区”。
(3) 常见陷阱与示例
基础示例
赋值陷阱
看似合法的赋值,实则是 TDZ。
函数参数默认值
typeof 陷阱
在 TDZ 中,连以前被认为是“安全”的操作 typeof 也会报错。
Class 同样受影响
类声明也不会像函数声明那样整体提升,必须先定义后使用。
3. 作用域链
在查找变量时,JS 引擎会先在当前作用域找,找不到就去外层作用域找,直到全局作用域。如果在全局还找不到,非严格模式下会自动创建全局变量(写入赋值),或者报错(读取)。
面试题:词法作用域 函数的作用域在定义时就确定了,而不是调用时。
答案:输出 2。因为 foo 是在全局作用域定义的,它的上层作用域是全局作用域(a=2),而不是 bar 的作用域。
4. 面试考点
Q: var, let, const 区别?
- 作用域:
var是函数作用域,let/const是块级作用域。 - 提升:
var提升并初始化为undefined;let/const提升但不初始化(TDZ)。 - 重复声明:
var允许,let/const报错。 - 全局对象挂载:
var a = 1会导致window.a === 1;let a = 1不会挂载到window。
Q: 立即执行函数 (IIFE) 的作用?
创建一个独立的函数作用域,避免污染全局变量。现代开发中多用模块化或块级作用域代替。

