标准答案

  1. 作用域:var 是函数作用域,在全局里声明就是全局作用域;let 和 const 是块级作用域,只在当前代码块里生效。
  2. 提升行为:var 声明会被提升,并且初始化为 undefined,所以声明前读取不会报错,只会得到 undefined。let 和 const 的声明也会在作用域创建时被登记,但在代码执行到声明语句前不能访问,这段区间叫暂时性死区,提前访问会抛 ReferenceError。
  3. 重复声明:var 在同一作用域里可以重复声明;let 和 const 不允许在同一作用域重复声明。
  4. 重新赋值:let 可以重新赋值;const 声明时必须初始化,并且变量不能再指向另一个值。如果 const 指向对象或数组,不能换成另一个引用,但对象内部属性仍然可以改。实际开发里一般优先用 const,需要重新赋值时用 let,尽量不用 var。

题目解析

这里不要只记“let/const 是块级作用域”。更关键的是解释变量在创建阶段和执行阶段的差异,尤其是为什么 var 可以提前读到 undefined,而 let/const 会进入暂时性死区。

const 的限制也容易被误解。它限制的是变量绑定不能重新指向,不是让引用值深度不可变。因此 const obj = {} 后,obj.name = "a" 是允许的,obj = {} 才是不允许的。

在 for 循环闭包场景里,用 let 时每轮迭代都会创建新的块级绑定,所以闭包拿到的是每轮自己的 i;var 则共享同一个函数级绑定。

代码示例

下面这段代码能把 var 提升、let 暂时性死区和 const 引用约束放在一起说明。

JavaScript
console.log(a) // undefined
var a = 1

// console.log(b) // ReferenceError
let b = 2

const user = { name: 'Alice' }
user.name = 'Bob' // 可以修改对象内部属性
// user = { name: 'Carol' } // TypeError

常见误区

  • 误以为 let 和 const 没有提升。更准确地说,它们的绑定会被创建,但在声明前处于暂时性死区,不能访问。
  • 误以为 const 声明的对象完全不可变。const 只限制变量绑定不能换引用。
  • 把变量提升理解成源码真的移动了。它本质上是执行上下文创建阶段先登记变量绑定。

作者信息