基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

JavaScript 中 var、let、const 详解

知识点图片

本文详细对比了JS中var, let, 和 const在作用域、变量提升、重复声明和重新赋值方面的核心区别,并给出了最佳实践。

我们来详细解析一下 JavaScript 中 var, let, 和 const 这三个关键字的区别。这是 JS 中一个非常基础且重要的概念,尤其是在 ES6 (letconst 被引入) 之后。

简单来说:

  • var: ES5 的变量声明方式,存在一些问题(如变量提升、无块级作用域)。
  • let: ES6 引入的块级作用域变量声明方式,修复了 var 的主要问题。
  • const: ES6 引入的块级作用域常量声明方式,声明后其值(内存地址)不能再改变。

下面我们通过一个对比表格和详细解释来深入理解。

核心区别速查表

特性 var let const
作用域 (Scope) 函数作用域 (Function Scope) 块级作用域 (Block Scope) 块级作用域 (Block Scope)
变量提升 (Hoisting) ,且初始化为 undefined ,但存在“暂时性死区” ,但存在“暂时性死区”
重复声明 允许在同一作用域内重复声明 不允许在同一作用域内重复声明 不允许在同一作用域内重复声明
重新赋值 允许 允许 不允许 (基本类型的值,或对象的引用)
声明时初始化 可不初始化 (默认为 undefined) 可不初始化 (默认为 undefined) 必须在声明时初始化
全局对象属性 在全局作用域声明时,会成为 window 对象的属性 在全局作用域声明时,不会成为 window 对象的属性 在全局作用域声明时,不会成为 window 对象的属性

详细解释与代码示例

1. 作用域 (Scope)

这是最核心的区别。

  • var (函数作用域)
    var 声明的变量只在它所在的函数内部有效。如果在函数外声明,它就是全局变量。它没有块级作用域的概念。

    javascript
    function testVar() {
      if (true) {
        var message = "Hello"; // message 在整个函数内都有效
      }
      console.log(message); // 输出 "Hello",变量"泄露"到了块外部
    }
    testVar();
    // console.log(message); // 报错: message is not defined,因为它在函数外部不可见

    for 循环中,这个问题尤其明显:

    javascript
    for (var i = 0; i < 3; i++) {
      setTimeout(function() {
        console.log(i); // 会连续输出 3, 3, 3
      }, 100);
    }
    // 因为循环结束时 i 的值是 3,而 setTimeout 的回调函数在循环结束后才执行,
    // 它们共享同一个函数作用域下的变量 i。
  • letconst (块级作用域)
    letconst 声明的变量只在它们所在的代码块({} 包裹的范围)内有效。

    javascript
    function testLet() {
      if (true) {
        let message = "Hello";
        const year = 2023;
        console.log(message); // 输出 "Hello"
      }
      // console.log(message); // 报错: message is not defined
      // console.log(year);    // 报错: year is not defined
    }
    testLet();

    let 解决 for 循环问题:

    javascript
    for (let i = 0; i < 3; i++) {
      // 每次循环都会创建一个新的块级作用域,i 的值被“锁”在当前循环中
      setTimeout(function() {
        console.log(i); // 会依次输出 0, 1, 2
      }, 100);
    }

2. 变量提升 (Hoisting) 与 暂时性死区 (TDZ)

  • var
    var 声明的变量会被提升到其作用域的顶部,并被初始化为 undefined。这意味着你可以在声明之前访问它,不会报错,只是值为 undefined

    javascript
    console.log(myVar); // 输出: undefined
    var myVar = "I am hoisted";
    console.log(myVar); // 输出: "I am hoisted"

    上面的代码在执行时,等同于:

    javascript
    var myVar; // 提升并初始化为 undefined
    console.log(myVar);
    myVar = "I am hoisted";
    console.log(myVar);
  • letconst
    letconst 声明的变量也会被提升,但它们不会被初始化。在代码运行到声明语句之前,任何对该变量的访问都会导致 ReferenceError。从作用域顶部到声明语句的这部分区域被称为“暂时性死区” (Temporal Dead Zone, TDZ)

    javascript
    // console.log(myLet); // 报错: ReferenceError: Cannot access 'myLet' before initialization
    let myLet = "I am not initialized";
    console.log(myLet); // 输出: "I am not initialized"
    
    // console.log(myConst); // 同样会报错
    const myConst = "Me too";

3. 重复声明 (Re-declaration)

  • var: 允许在同一个作用域内重复声明同一个变量,后面的声明会覆盖前面的。

    javascript
    var name = "Alice";
    var name = "Bob"; // 合法,name 的值现在是 "Bob"
    console.log(name); // 输出: "Bob"
  • letconst: 在同一个作用域内,不允许重复声明同一个变量,否则会抛出 SyntaxError

    javascript
    let age = 30;
    // let age = 31; // 报错: SyntaxError: Identifier 'age' has already been declared
    
    const country = "USA";
    // const country = "Canada"; // 同样会报错

4. 重新赋值 (Re-assignment)

  • varlet: 允许被重新赋值。

    javascript
    var a = 1;
    a = 2; // 可以
    
    let b = 1;
    b = 2; // 也可以
  • const: 声明的是一个常量。一旦声明,就不能再被重新赋值。

    javascript
    const PI = 3.14;
    // PI = 3.14159; // 报错: TypeError: Assignment to constant variable.

    重要注意事项: 当 const 声明的是一个对象或数组时,它保护的是变量的引用(内存地址),而不是引用的值(对象或数组的内容)。这意味着你不能让这个变量指向另一个对象,但你可以修改该对象内部的属性。

    javascript
    const person = {
      name: "Alice"
    };
    
    // 合法操作:修改对象内部的属性
    person.name = "Bob";
    person.age = 25;
    console.log(person); // 输出: { name: "Bob", age: 25 }
    
    // 错误操作:尝试将 person 指向一个新对象
    // person = { name: "Charlie" }; // 报错: TypeError: Assignment to constant variable.

总结与最佳实践

在现代 JavaScript 开发中,遵循以下原则是最佳实践:

  1. 优先使用 const: 默认使用 const 来声明所有变量。这能让你的代码更具可预测性,因为你知道这个变量的引用不会改变。
  2. 当需要重新赋值时,才使用 let: 只有当你明确知道一个变量的值在未来需要被改变时(例如,循环中的计数器或一个状态标志),才使用 let
  3. 避免使用 var: 在新的代码中完全避免使用 var。它带来的作用域和变量提升问题很容易导致难以察觉的 bug。letconst 是更安全、更现代的选择。
00:00
00:00