创见博客
let、var、const 的区别
七崽爱吃小饼干2026/10/07阅读 0

同一个变量,用三种声明写出来,行为能差到让人怀疑人生:

js
var a = 1
let b = 2
const c = 3

console.log(window.a) // 1,var 挂到了全局对象上
console.log(window.b) // undefined,let 不会
console.log(c)        // 3

let、const、var 的区别不只是"能不能改",它牵涉到作用域、变量提升、暂时性死区、全局对象挂载这几件事。这篇文章按这几个维度逐一拆开,最后给一张速查表和选型建议。

一、作用域:函数作用域 vs 块级作用域

var 是函数作用域,只认函数的大括号,不认 if、for 这种块:

js
function fn() {
  if (true) {
    var x = 1
  }
  console.log(x) // 1,块外依然能访问
}
fn()

let 和 const 是块级作用域,任何一对 {} 都是一道墙:

js
if (true) {
  let x = 1
  const y = 2
}
console.log(x) // ReferenceError: x is not defined
console.log(y) // ReferenceError: y is not defined

这个差异在循环里最经典,也是当初引入 let 的核心动机:

js
// var:三个闭包共用同一个 i,最终都输出 3
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i)) // 3 3 3
}

// let:每次迭代生成新的绑定,各自记住自己的 i
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i)) // 0 1 2
}

二、变量提升与暂时性死区

三者都有"提升",但表现完全不同。

var 的声明会被提升,并初始化为 undefined:

js
console.log(a) // undefined,不报错
var a = 1
// 等价于
// var a
// console.log(a)
// a = 1

let 和 const 也会提升,但不会初始化,在声明之前访问会进入"暂时性死区"(Temporal Dead Zone,TDZ),直接抛错:

js
console.log(b) // ReferenceError: Cannot access 'b' before initialization
let b = 2

console.log(c) // ReferenceError: Cannot access 'c' before initialization
const c = 3

TDZ 的存在让"先使用后声明"这种错误在开发期就暴露,而不是静静地拿到一个 undefined。

三、重复声明与重新赋值

行为varletconst
重复声明同名变量✅ 允许❌ 报错❌ 报错
重新赋值✅✅❌
声明时必须初始化❌❌✅
js
// var 允许重复声明,后面的覆盖前面的
var a = 1
var a = 2
console.log(a) // 2

// let 不允许在同一作用域重复声明
let b = 1
let b = 2 // SyntaxError: Identifier 'b' has already been declared

// const 声明即赋值,且不能再指向别的值
const c = 1
c = 2 // TypeError: Assignment to constant variable
const d // SyntaxError: Missing initializer in const declaration

四、const 不等于"值不可变"

这是最容易误解的一点。const 锁的是绑定(引用),不是里面的内容。对原始类型,它确实不可变;对引用类型,属性照样能改:

js
const arr = [1, 2, 3]
arr.push(4)      // ✅ 可以,修改的是数组内容
console.log(arr) // [1, 2, 3, 4]
arr = [5]        // ❌ TypeError,不能重新指向新数组

const obj = { count: 1 }
obj.count = 2    // ✅ 可以
obj = {}         // ❌ TypeError

想要真正冻结对象,需要 Object.freeze(注意它是浅冻结):

js
const obj = Object.freeze({ count: 1 })
obj.count = 2
console.log(obj.count) // 1,静默失败(严格模式抛错)

五、全局声明时挂到哪

在全局作用域下声明:

  • var 会把变量挂到全局对象 window 上,成为它的属性。
  • let / const 不会挂到 window,它们存在于全局的词法环境中。
js
var a = 1
let b = 2
const c = 3

window.a // 1
window.b // undefined
window.c // undefined

这带来一个实际差异:var 声明的全局变量会成为 window 的属性,可能覆盖已有属性;let / const 更"干净"。另外在同一全局下,let 声明的名字如果和 window 上已有的不可配置属性同名,会直接报错。

六、怎么选:一条简单的规则

  1. 默认用 const。绝大多数变量声明后不需要换指向,用它最安全,也向读代码的人表达"这里不会变"。
  2. 需要重新赋值时用 let,比如循环计数器、累加器、状态变量。
  3. var 基本不用。只在需要兼容极老环境或要利用"函数作用域提升"的特殊写法时才考虑,新代码里应避免。
js
// 推荐写法
const list = getList()
let total = 0
for (const item of list) {
  total += item.price
}

速查表

维度varletconst
作用域函数作用域块级作用域块级作用域
变量提升提升并初始化为 undefined提升但不初始化(TDZ)提升但不初始化(TDZ)
暂时性死区无有有
重复声明允许不允许不允许
重新赋值允许允许不允许
必须初始化否否是
挂到全局对象是否否

小结

  • 核心区别是作用域:var 认函数,let / const 认块。
  • let / const 有暂时性死区,声明前访问直接报错,比 var 拿到 undefined 更安全。
  • const 锁的是引用,不是内容,对象和数组内部仍可修改。
  • 全局下 var 会挂到 window,let / const 不会。
  • 选型一句话:能 const 就 const,要变用 let,var 别用。
评论
0/100