聊一下一个老掉牙的话题:let、const与var的区别与联系

101 阅读3分钟

let、const与var的区别与联系详解

一、核心差异对比

  1. ​作用域机制​

    • ​var​​:函数作用域(在函数内声明的变量在整个函数有效)
    • ​let/const​​:块级作用域(在{}内声明的变量仅在该块有效)
      示例差异
    if (true) {
      var a = 1;  // 全局作用域
      let b = 2;  // 块级作用域
    }
    console.log(a); // 1(可访问)
    console.log(b); // ReferenceError(不可访问)
    
  2. ​变量提升行为​

    • ​var​​:声明提升(变量可提前访问,值为undefined
    • ​let/const​​:存在暂时性死区(TDZ),声明前访问报错
      代码验证
    console.log(x); // undefined(var提升)
    var x = 10;
    
    console.log(y); // ReferenceError(let未提升)
    let y = 20;
    
  3. ​重复声明限制​

    • ​var​​:允许同一作用域内重复声明
    • ​let/const​​:严格禁止重复声明
      典型错误
    var count = 1;
    var count = 2;  // 允许(覆盖原值)
    
    let age = 25;
    let age = 30;   // SyntaxError
    
  4. ​可变性与赋值规则​

    • ​var/let​​:可随时重新赋值
    • ​const​​:声明时必须初始化,且不可重新赋值(但对象属性可修改)
      特殊案例
    const PI = 3.14;
    PI = 3.1415;    // TypeError
    
    const user = { name: "Alice" };
    user.name = "Bob";  // 允许(修改属性)
    user = {};        // TypeError(重新赋值)
    

二、深层联系与设计逻辑

  1. ​历史演进关系​

    • var是ES5及之前的唯一变量声明方式
    • letconst是ES6引入的块级作用域解决方案
    • 设计目标:解决var的作用域污染和变量提升问题
  2. ​内存管理差异​

    关键字内存分配时机生命周期
    var函数执行时函数结束
    let块级开始块级结束
    const块级开始块级结束
  3. ​循环场景的特殊表现​

    for (var i = 0; i < 3; i++) {
      setTimeout(() => console.log(i), 100);  // 输出 3,3,3(变量共享)
    }
    
    for (let j = 0; j < 3; j++) {
      setTimeout(() => console.log(j), 100);  // 输出 0,1,2(独立作用域)
    }
    

三、最佳实践指南

  1. ​使用优先级建议​

image.png

  1. ​场景化选择策略​

    • ​const​​:数学常量、配置对象、DOM引用
    • ​let​​:循环计数器、状态变量、累加器
    • ​var​​:仅限需要函数作用域的特殊场景
  2. ​代码规范建议​

    • 禁止使用var声明函数外变量
    • 使用ESLint强制块级作用域规则
    • 复杂类型用const声明,通过解构赋值修改属性

四、常见问题解析

  1. ​变量提升的底层原理​

    • var声明会被提升到函数/全局作用域顶部
    • let/const声明记录在词法环境,但初始化阶段标记为"未完成"
  2. ​暂时性死区的具体表现​

    // TDZ验证
    console.log(foo); // ReferenceError
    let foo = 3;
    
    // typeof检测也会报错
    typeof bar;     // ReferenceError
    let bar = 5;
    
  3. ​块级作用域的嵌套规则​

    {
      let outer = "外层";
      if (true) {
        let inner = "内层";
        console.log(outer); // 允许(外层可见内层)
      }
      console.log(inner); // 报错(内层不可见外层)
    }
    

五、现代开发中的演进

  1. ​TypeScript的增强支持​

    • readonly修饰符提供更严格的不可变性
    • const断言(as const)实现深层冻结
  2. ​框架层面的优化​

    • React Hooks依赖let实现状态更新
    • Vue3响应式系统深度依赖块级作用域
  3. ​未来发展方向​
    -提案中的let块外声明限制

    • 增强的常量检测机制(如对象属性冻结)

通过合理运用这三类声明,开发者可以显著提升代码的可维护性和健壮性。现代JavaScript开发中,建议遵循"默认使用const,必要时用let,尽量避免var"的原则。