Skip to content

变量与声明

变量用于给数据取名,并在后续代码里反复引用。JavaScript 目前最常见的声明方式有三种:letconstvar

为什么需要变量

变量的作用不是“把值存起来”这么简单,它更重要的价值是:

  • 给数据一个稳定名字
  • 在不同语句之间共享结果
  • 让表达式更易读、可复用
js
const price = 100
const count = 2
const total = price * count

三种声明方式

let

let 用于声明一个可以重新赋值的变量。

js
let message = 'hello'

message = 'world'

适合:

  • 后续会发生变化的状态
  • 循环计数器
  • 条件分支里需要重新赋值的变量

const

const 用于声明一个不能被重新赋值的绑定。

js
const siteName = 'vfan docs'

// siteName = 'other' // TypeError

需要注意,const 约束的是“变量绑定不能改”,不等于“值一定完全不可变”。

js
const user = {
  name: 'Lee',
}

user.name = 'Vfan'

这里修改的是对象内部属性,不是重新给 user 赋新值,所以是允许的。

var

var 是早期 JavaScript 的变量声明方式,现在只在兼容旧代码或理解历史代码时需要重点掌握。

js
var count = 1

现代开发里通常不再主动使用 var,主要原因是它的作用域和提升规则更容易引发误解。

letconstvar 的主要区别

是否允许重新赋值

  • let:允许
  • const:不允许
  • var:允许

作用域

  • let:块级作用域
  • const:块级作用域
  • var:函数作用域,不具备真正的块级作用域
js
if (true) {
  let a = 1
  const b = 2
  var c = 3
}

// console.log(a) // a is not defined
// console.log(b) // b is not defined
console.log(c) // 3

重复声明

js
let value = 1
// let value = 2 // SyntaxError
js
var count = 1
var count = 2

var 允许同一作用域内重复声明,这也是它更容易制造隐藏问题的原因之一。

提升与暂时性死区

var 的提升

var 声明会在进入作用域时先被创建,因此声明前访问它不会报“未定义变量”,而是得到 undefined

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

这等价于概念上的:

js
var a
console.log(a)
a = 1

letconst 的暂时性死区

letconst 也会进入作用域,但在真正执行到声明语句之前,不能被访问。

js
{
  // console.log(count) // Cannot access 'count' before initialization
  const count = 1
}

这段声明前不可访问的区域,通常称为暂时性死区。

const 一定要初始化

const 声明时必须同时赋值:

js
const id = 1

下面这种写法会报错:

js
// const id

因为 const 不允许先声明、后赋值。

命名约定

变量名通常需要满足以下规则:

  • 可以包含字母、数字、$_
  • 不能以数字开头
  • 不能使用保留字

常见约定:

  • 普通变量使用小驼峰,例如 userName
  • 构造函数或类使用大驼峰,例如 UserProfile
  • 真正意义上的常量通常用全大写加下划线,例如 MAX_RETRY_COUNT

常见误区

const 不是“深度不可变”

js
const config = {
  retry: 3,
}

config.retry = 5

如果需要真正冻结对象,还要结合 Object.freeze() 等手段,但它也只是一层冻结。

不要为了“以后可能改”就默认全用 let

如果一个绑定本来就不需要重新赋值,优先用 const。这样可以更清楚地表达意图,也能减少无意修改。

var 在循环里容易出问题

js
for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i)
  }, 0)
}

结果通常是:

js
3
3
3

因为所有回调共享同一个 i 绑定。改成 let 后,每次迭代都会生成新的绑定:

js
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i)
  }, 0)
}

使用建议

  • 默认优先使用 const
  • 只有在明确需要重新赋值时,才使用 let
  • 现代代码中尽量避免新增 var
  • 当看到声明前访问、循环闭包、重复声明这类问题时,优先排查是不是 var 导致的。

基于 MIT 许可发布