JavaScript 变量声明:从 “混乱” 到 “规矩”,一文搞懂 var 和 let

0 阅读4分钟

一、先搞懂 JS 代码的 “两步走” 逻辑

JavaScript 代码运行时就像一场 “先准备后表演” 的舞台剧:

  1. 准备阶段(编译期)
  • 扫描所有变量(var)和函数声明,把它们 “提前放到舞台上”(提升到作用域顶部),但var变量先空着(初始值是undefined),函数则完整放上去。
  • 举个栗子
sayHi() // 能直接调用,因为函数被提前“放上台”了  
var name = "小明" // 变量声明被提前,但赋值还在原地  
function sayHi() { console.log("Hi, " + name) }
  • 等价于
var name; // 先声明变量  
function sayHi() { ... } // 先声明函数  
sayHi() // 此时name是undefined,输出“Hi, undefined” 
name = "小明" // 最后赋值 
  1. 表演阶段(执行期)
    按顺序执行赋值、运算等操作,变量才真正有值。

二、var 的 “混乱时代”:到处乱窜的变量

1. 作用域像 “大通铺”:只认函数,不认块

  • var声明的变量要么在整个函数里都能访问(函数作用域),要么在整个程序里都能访问(全局作用域),但不认if/for等花括号{}的块级作用域
  • 例子
if (true) {  
  var age = 18 // 在函数里声明,块外也能访问  
}  
console.log(age) // 输出18,块级作用域“拦不住”var  

2. 变量能 “提前见面”,但值是 “空的”

  • var变量会被 “提前声明”,但赋值还在原地,所以声明前访问会得到undefined(不会报错)。
  • 例子
console.log(num) // 输出undefined(提前声明了,但没赋值)  
var num = 100 

3. 允许 “重名” 变量:后面的覆盖前面的

  • 同一个作用域里可以用var多次声明同一个变量,后面的声明会覆盖前面的。
var car = "自行车"  
var car = "汽车" // 允许重复声明,值变成“汽车” 

三、let 的 “规矩时代”:块级作用域 + 严格限制

1. 作用域像 “单人房间”:只认块级花括号{}

  • let声明的变量只能在声明它的花括号{}里访问(块级作用域),出了这个块就 “消失” 了。

  • 例子

for (let i = 0; i < 3; i++) {  
  // i只在for循环的花括号里有效  
}  
console.log(i) // 报错!i已经“消失”了  

2. 禁止 “提前见面”:声明前访问会 “挨打”

  • let变量不会被提前声明,在声明之前的区域(暂时性死区)访问会直接报错(ReferenceError)。
console.log(book) // 报错!不能在声明前访问  
let book = "JavaScript入门"

3. 禁止 “重名”:同一个作用域里只能声明一次

  • 同一个作用域里用let重复声明同一个变量,代码直接报错(编译阶段就失败)。

  • 例子

let cat = "小白"  
let cat = "小黑" // 报错!“cat”已经声明过了  

四、var vs let 对比表:一看就懂

特性var(混乱时代)let(规矩时代)
作用域范围函数或全局(不认{}块级作用域)仅限声明的{}块级作用域
提前访问可以访问,值是undefined不能访问,会报错(暂时性死区)
重复声明允许(后面的覆盖前面的)禁止(编译阶段报错)
全局变量影响全局声明会变成全局对象的属性不会成为全局对象属性
适合场景老旧代码兼容,极少数特殊场景现代开发,推荐优先使用

五、新手如何避免踩坑?记住这 3 条

  1. 能用let/const就不用var

    • let适合值会变的变量,const适合值不变的常量(比如配置项)。
  2. 变量声明尽量靠近使用的地方

    • 别在作用域顶部声明一堆暂时用不到的变量,代码会更清晰。
  3. {}块级作用域 “圈住” 变量

    • 比如在if/for里用let声明变量,避免变量 “乱跑” 污染其他地方。

六、为什么 JS 要设计两种变量声明?

  • var是历史遗留产物:早期 JS 没有块级作用域,var的设计是为了兼容老代码。

  • let是 ES6 的新规范:让 JS 更严谨,减少变量作用域混乱导致的 bug,更符合现代编程习惯。

总结var像 “放养的野马”,容易失控;let像 “圈养的绵羊”,规矩但安全。新手入门直接用letconst,基本能解决 90% 的作用域问题