Skip to content

let 和 const 命令

ES6 引入了 letconst 两个新的变量声明关键字,彻底改变了 JavaScript 的变量作用域规则。

🎯 var 的问题#

先看 var 存在的问题:

// 问题1:变量提升
console.log(name) // undefined,而不是报错
var name = '张三'
// 问题2:没有块级作用域
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100)
}
// 输出:3, 3, 3(而不是 0, 1, 2)
// 问题3:可以重复声明
var count = 1
var count = 2 // 不报错,容易造成 bug

let 声明#

基本用法#

let age = 25
age = 26 // 可以重新赋值
let username = '李四'
// let username = '王五'; // SyntaxError: 不能重复声明

块级作用域#

let 声明的变量只在当前代码块内有效:

{
let a = 10
var b = 20
}
// console.log(a); // ReferenceError: a is not defined
console.log(b) // 20

经典的循环问题用 let 轻松解决:

for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100)
}
// 输出:0, 1, 2 ✅

暂时性死区(TDZ)#

从块的开始到 let 声明之间的区域叫做”暂时性死区”(Temporal Dead Zone):

// TDZ 开始
console.log(x) // ReferenceError: Cannot access 'x' before initialization
let x = 10 // TDZ 结束

🔶 TDZ 的存在让代码更加可预测,强制你先声明后使用。

一个隐蔽的 TDZ 场景:

function foo(x = y, y = 2) {
return [x, y]
}
foo() // ReferenceError: y 在 TDZ 中

const 声明#

基本用法#

const 声明一个只读常量,必须在声明时初始化:

const PI = 3.14159
// PI = 3.14; // TypeError: Assignment to constant variable
// const MAX; // SyntaxError: Missing initializer

对象和数组#

🤔 const 保证的是变量指向的内存地址不变,而不是值不变:

const user = { name: '张三' }
user.name = '李四' // ✅ 可以修改属性
user.age = 25 // ✅ 可以添加属性
// user = {}; // ❌ TypeError: 不能重新赋值
const list = [1, 2, 3]
list.push(4) // ✅ 可以修改数组内容
// list = []; // ❌ TypeError

如果需要真正不可变的对象,使用 Object.freeze()

const frozen = Object.freeze({ name: '张三' })
frozen.name = '李四' // 静默失败,严格模式下报错
console.log(frozen.name) // '张三'
// 注意:freeze 是浅冻结
const nested = Object.freeze({
info: { age: 25 },
})
nested.info.age = 30 // 仍然可以修改
console.log(nested.info.age) // 30

深度冻结需要递归处理:

function deepFreeze(obj) {
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === 'object' && obj[key] !== null) {
deepFreeze(obj[key])
}
})
return Object.freeze(obj)
}
const config = deepFreeze({
api: { url: 'https://api.example.com' },
})
config.api.url = 'xxx' // 无效

块级作用域的应用#

替代 IIFE#

ES5 时代用 IIFE 创建私有作用域:

// ES5
;(function () {
var private = '私有变量'
})()

ES6 直接用块:

// ES6
{
let private = '私有变量'
}

条件声明#

function process(condition) {
if (condition) {
let result = '处理结果'
return result
}
// result 在这里不可访问,避免意外使用
return null
}

switch 语句#

// 🔶 没有块作用域会报错
switch (type) {
case 'A':
let msg = 'Type A' // 报错:重复声明
break
case 'B':
let msg = 'Type B'
break
}
// ✅ 正确写法:用块包裹
switch (type) {
case 'A': {
let msg = 'Type A'
console.log(msg)
break
}
case 'B': {
let msg = 'Type B'
console.log(msg)
break
}
}

全局变量#

varfunction 声明的全局变量会成为 window 的属性,letconst 不会:

var globalVar = 'var'
let globalLet = 'let'
const globalConst = 'const'
console.log(window.globalVar) // 'var'
console.log(window.globalLet) // undefined
console.log(window.globalConst) // undefined

最佳实践#

1. 默认使用 const#

// ✅ 推荐
const config = { debug: true }
const API_URL = 'https://api.example.com'
const numbers = [1, 2, 3]
// 只有需要重新赋值时才用 let
let count = 0
count++

2. 不再使用 var#

// ❌ 避免
var name = '张三'
// ✅ 推荐
const name = '张三'

3. 在块的顶部声明#

function process(items) {
// 变量声明放在块的顶部
const result = []
let total = 0
for (const item of items) {
result.push(item.value)
total += item.value
}
return { result, total }
}

4. 循环中的选择#

// for...of 和 for...in 使用 const
for (const item of items) {
console.log(item)
}
for (const key in obj) {
console.log(key)
}
// 传统 for 循环使用 let
for (let i = 0; i < 10; i++) {
console.log(i)
}

常见问题#

🙋 let 和 const 有变量提升吗?#

有,但行为不同。它们会被提升到块的顶部,但在声明之前处于 TDZ,访问会报错:

// JavaScript 引擎知道 x 存在,但不让你访问
console.log(x) // ReferenceError
let x = 1

🙋 什么时候用 let?#

只有在需要重新赋值时才用 let:

// 计数器
let count = 0
items.forEach(() => count++)
// 累加器
let sum = 0
for (const num of numbers) {
sum += num
}
// 状态标记
let isReady = false
// ... 某些操作后
isReady = true

🙋 const 声明的对象如何实现不可变?#

// 方案1:Object.freeze()(浅冻结)
const obj = Object.freeze({ a: 1 })
// 方案2:深冻结函数
// 方案3:使用 Immutable.js 等库
// 方案4:TypeScript 的 Readonly 类型(编译时检查)

总结对比#

特性varletconst
作用域函数作用域块级作用域块级作用域
变量提升是(初始化为 undefined)是(但有 TDZ)是(但有 TDZ)
重复声明允许不允许不允许
重新赋值允许允许不允许
全局属性是(挂载到 window)
必须初始化

结论:默认使用 const,需要重新赋值时使用 let,不再使用 var