JavaScript 中 var 与 let 区别详解(通俗版)

1. 文档前言

本文档旨在用通俗语言+代码示例+生活类比的方式,帮助 JavaScript 初学者清晰理解 varlet 两种变量声明方式的核心区别,避免实际编码中因变量作用域问题导致的 bug。

2. 核心区别解析

2.1 区别1:作用域范围(变量能被“看到”的区域)

2.1.1 var:函数作用域(“大大咧咧”,范围广)
  • 通俗解释:用 var 声明的变量,只要在同一个函数里,不管藏在多少层 {}(如 if、for 块)里,整个函数内的代码都能“看到”它。
  • 代码示例
    function 家里() {
      // if 块相当于“卧室”
      if (天气好) {
        var 玩具 = "积木"; // 用 var 声明玩具
      }
      // 出了卧室(if块),还能访问到玩具
      console.log(玩具); // 输出:积木(不报错)
    }
    家里();
    
  • 生活类比:你在卧室藏了个玩具,家里所有人(整个函数)都能找到它,不会因为玩具在卧室就找不到。
2.1.2 let:块级作用域(“有规矩”,范围小)
  • 通俗解释:用 let 声明的变量,只在它所在的那对 {} 里有效,出了这对 {},代码就“看不到”它了。
  • 代码示例
    function 家里() {
      // if 块相当于“卧室”
      if (天气好) {
        let 零食 = "薯片"; // 用 let 声明零食
      }
      // 出了卧室(if块),找不到零食
      console.log(零食); // 报错:Uncaught ReferenceError: 零食 is not defined
    }
    家里();
    
  • 生活类比:你在卧室藏了包薯片,只有在卧室里(当前 {} 内)能找到,出了卧室就没人知道薯片在哪了。

2.2 区别2:提前使用(变量声明前能否访问)

2.2.1 var:可以提前用,但值是 undefined
  • 通俗解释var 声明的变量会被“偷偷提到”当前作用域的最前面(专业叫“变量提升”),所以能在声明代码之前用它,但此时变量还没赋值,值是 undefined
  • 代码示例
    // 声明前访问变量
    console.log(苹果); // 输出:undefined(能访问,但没值)
    // 正式声明变量
    var 苹果 = "红富士";
    
  • 生活类比:妈妈早上说“下午买苹果”,你早上就能跟别人提“家里要吃苹果”,但此时苹果还没买回来(值为 undefined)。
2.2.2 let:提前用会直接报错(“暂时性死区”)
  • 通俗解释let 声明的变量也会“提升”,但在正式声明代码执行前,变量处于“禁区”(专业叫“暂时性死区”),谁都不能碰,一碰就报错。
  • 代码示例
    // 声明前访问变量
    console.log(香蕉); // 报错:Uncaught ReferenceError: Cannot access '香蕉' before initialization
    // 正式声明变量
    let 香蕉 = "黄香蕉";
    
  • 生活类比:妈妈还没说要给你买香蕉,你就跟别人说“家里有香蕉”,妈妈会直接纠正你:“哪来的香蕉?还没买呢!”(报错)。

2.3 区别3:重复声明规则(同一范围能否重名)

2.3.1 var:允许重复声明,后面的覆盖前面的
  • 通俗解释:在同一个作用域里,var 可以给同一个变量名重复赋值,后面的赋值会把前面的“覆盖”掉,不会报错。
  • 代码示例
    var 宠物 = "猫"; // 第一次声明:宠物是猫
    var 宠物 = "狗"; // 第二次声明:宠物改成狗(不报错)
    console.log(宠物); // 输出:狗(后面的覆盖了前面的)
    
  • 生活类比:你先给宠物起名“咪咪”(猫),后来觉得不好听,改成“旺财”(狗),家里人都会认“旺财”这个新名字。
2.3.2 let:禁止重复声明,重名直接报错
  • 通俗解释:在同一个作用域里,let 不允许给同一个变量名重复声明,哪怕想改值也不能用 let 重写,否则直接报错。
  • 代码示例
    let 宠物 = "猫"; // 第一次声明:宠物是猫
    let 宠物 = "狗"; // 第二次声明:直接报错
    // 报错:Uncaught SyntaxError: Identifier '宠物' has already been declared
    
  • 生活类比:你已经给宠物起名“咪咪”了,再想让它叫“旺财”,家里人会说:“不行!名字不能随便重起,只能叫咪咪!”(报错)。

2.4 区别4:全局作用域中的特殊表现(与 window 对象的关系)

在浏览器的全局环境(不在任何函数里)中,varlet 还有个关键区别:

2.4.1 var:全局变量会变成 window 的属性
  • 通俗解释:用 var 声明的全局变量,会自动“绑定”到浏览器的 window 对象上,成为 window 的一个属性。
  • 代码示例
    var 全局变量_var = 100; // 全局声明 var 变量
    console.log(window.全局变量_var); // 输出:100(变量成了 window 的属性)
    
2.4.2 let:全局变量不会绑定到 window
  • 通俗解释:用 let 声明的全局变量,只是单纯的“全局变量”,不会和 window 产生关联,window 上找不到它。
  • 代码示例
    let 全局变量_let = 200; // 全局声明 let 变量
    console.log(window.全局变量_let); // 输出:undefined(和 window 没关系)
    

3. 核心区别总结表

对比维度 var 特性 let 特性
作用域范围 函数作用域(函数内都可见) 块级作用域(仅所在 {} 内可见)
提前使用(声明前访问) 允许,值为 undefined 禁止,报错(暂时性死区)
同一作用域重复声明 允许,后面覆盖前面 禁止,直接报错
全局环境与 window 关系 会成为 window 的属性 不会绑定到 window

4. 推荐实践

在 ES6(2015年)之后,let 解决了 var 导致的很多问题(如变量泄漏、重复声明覆盖等),因此:

  1. 优先使用 let 声明“会变的值”(如循环变量、需要修改的状态);
  2. 若变量值不会变(如常量),用 const(和 let 作用域规则一致,只是不能改值);
  3. 尽量避免使用 var,减少代码出错概率。

更多推荐