ES6-深入浅出了解let和const前世今生

205 阅读5分钟

前言

  作为前端经典八股文之一,ES6新特性之 letconst 可谓各大面试常客之一,在 ECMAScript 6之前,声明变量使用 var 关键字,ECMAScript 6 新增了 const 和 let 关键字用以弥补var关键字的缺陷。

一、var 的缺点

1. var 允许重复声明变量

  使用var语句重复声明同一个变量不仅是合法的,也不会造成任何错误,会导致数据被覆盖,这在其他语言中是十分少见的。

  var a = "VeryCool";
  var a = "稀土掘金";
  console.log(a); // 输出稀土掘金

2. var 会污染全部变量

  var 定义的局部变量会自动添加全局对象 window,造成全局对象被污染,变得难以维护。

  var VeryCool  = '稀土掘金';
  console.log(window);

  此时打印window可以发现声明的变量已被挂载到全局对象。

image.png

3. var 存在变量提升

  无论我们在全局作用域还是在局部作用域中,使用var关键字声明的变量,都会被提升到该作用域的最顶部,这就是所谓的"变量提升"。

  console.log(VeryCool);    // 输出undefined,说明变量已被声明,还未赋值
  var VeryCool = "稀土掘金";

image.png 😕原因: JavaScript引擎解析分为变量声明和变量赋值两个阶段,首先会将所有被声明的变量提升到作用域顶部,再按顺序执行代码。上述代码执行过程如下:

  var VeryCool;           // 1.先声明变量
  console.log(VeryCool);  // 2.打印变量
  VeryCool = "稀土掘金";   // 3.再为变量赋值

⚠注意: 函数声明也会提升,并且优先级高于变量提升(先声明函数,再声明变量)

  fun();
  console.log(VeryCool);
  var VeryCool = "稀土掘金";
  function fun() {
    console.log("点个赞吧!");
  }

上述代码会先输出点个赞吧!再输出undefined,其执行过程如下:

  function fun() {             // 1.先声明函数
    console.log("点个赞吧!");
  }
  var VeryCool;                // 2.再声明变量
  fun();                       // 3.先调用函数
  VeryCool = "稀土掘金";        // 4.再为变量赋值

💣变量提升缺点:

1.无用的变量不能被及时销毁(如for循环结束后,变量 i 依然存在)

2.同名变量可能被无意间覆盖

二、let 和 const 优点

1. 暂时性死区(Temporal Dead Zone)

😊理解: letconst声明的变量从一开始就形成了封闭作用域,在代码未执行到变量初始化(首次赋值)之前,对变量进行访问或者赋值就会报错。所以使用letconst变量初始化之前,都是不可用的,称之为暂时性死区。

  console.log(VeryCool);    // 输出:ReferenceError:初始化之前无法访问“VeryCool”
  let VeryCool = "稀土掘金";

image.png

2. 块级作用域

😊理解: letconst声明的变量是块级作用域,即仅在当前 { } 中有效,无法从块的外部访问块内部的变量。而var在函数内部声明的变量是函数作用域,在函数内部都可以访问到。(var在全局声明的变量,是全局作用域)

  function fun() {
    {
      var Very = "稀土";
      let Cool = "掘金"
    }
    console.log(Very);  // 输出稀土
    console.log(Cool);  // 输出Cool is not defined
  }
  fun()

😃优点:

1.避免变量冲突,块内声明的变量仅在块内有效,将私有变量和全局对象分离(块外无法访问块内变量,块内变量也会覆盖块外同名变量),防止同名变量冲突。

2.利于内存回收,防止私有变量泄露为全局变量。(如for循环的 i )

3. 禁止重复声明

😊理解: 在同一作用域内,无法重复声明同一个变量。

  let VeryCool = "稀土";
  let VeryCool = "掘金";
  console.log(VeryCool); // 输出:已声明标识符"VeryCool"

image.png

4. 不会全局挂载

😊理解: var在全局作用域中声明的变量会挂载到window上称为window对象的属性,而let不会。

  var Very = "稀土";
  let Cool = "掘金";
  console.log(window); // 仅Very被挂载

image.png

三、const 和 let 区别

😊理解: const 声明的变量也是块级作用域不存在变量提升拥有暂时性死区,与 let 基本相同。

🆚不同的是: 

1.const 声明的是一个只读常量,不能修改。

  const VeryCool = "稀土";
  VeryCool = "掘金";
  console.log(VeryCool); // 输出:禁止给常量变量赋值

image.png

2.const 声明的常量在声明时就必须要赋值,不能只声明不赋值。

  const VeryCool;
  console.log(VeryCool); // 输出:常量声明中缺少初始值设定

image.png

四、const常量也可以被修改

🤔在实际开发中,经常可以发现,用const定义的对象或数组可以被修改,如下:

  const VeryCool = {
    name: "稀土掘金",
    age: 18,
  };
  VeryCool.age = 20;
  console.log(VeryCool); // 输出age为20

image.png

💡原因: const声明只限制了他所声明的变量指向的内存地址不变。

const定义的常量是基本数据类型(数值、字符串、布尔值),由于数据保存在栈中,所以无法修改变量。

const定义的常量是引用数据类型,仅在栈中保存指向堆内存的指针,const只能限制这个指针所指向的堆内存地址不变,而无法限制堆中的数据是否改变,所以修改引用数据类型内部的属性,不会受到const限制。

const.jpg

总结

  1. var允许变量提升,函数作用域,可以重复声明。
  1. let不允许变量提升,块级作用域,不能重复声明,不会全局挂载。
  1. const不允许变量提升,块级作用域,不能重复声明,不会全局挂载,声明基本数据类型禁止修改,声明引用数据类型可以修改。

结尾.jpg