带你了解ES6 let 和 const 命令,建议收藏

let 和 const 命令

1、let的基本用法与块级作用域

用法与var类似,但是let所声明的变量,只在命令所在的代码块内有效。

    {
        var a = 1;
        let b = 2;
    } 
    console.log(a) // 1
    console.log(b) //  b is not defined
如上面代码所示,在花括号外打印let声明的变量,会报错,而var可正常显示,说明let只在命令所在的代码块内有效

举个栗子

    for (let i = 0; i < 5; i++) {}
    console.log(i) //i is not defined

上面代码中,计数器i只在for循环体内有效,在循环体外引用就会报错。

     for (var i = 0; i < 5; i++) {
        setTimeout(() => console.log(i), 0)
         i //55555
    }

上面代码中,变量i是var声明的,在全局范围内都有效,所以全局只有一个变量i。而setTimeout是一个异步行为,而js执行机制是单线程的,代码的执行是从上到下,依次执行,碰到异步的代码会将其插入到任务队列当中等待,直到同步代码都执行完毕之后,任务队列中的异步代码才依次加载,而这时候全局变量i的值已经变成5了,所以打印出来的i全都为5

for (let j = 0; j < 5; j++) {
        setTimeout(() => console.log(j), 0)
        j // 0 1 2 3 4
    }
    //你也可以当成是声明了五个let块级作用域
    {let i = 0}
    {let i = 1}
    {let i = 2}
    {let i = 3}
    {let i = 4}

变量 j 是用 let 声明的,当前的 j 只在本轮循环中有效,每次循环的 j 其实都是一个新的变量,所以 setTimeout 定时器里面的 j 其实是不同的变量,即最后输出 01234。

    function f1() {
         let n = 5;
         if (true) {
             let n = 10;
             let m = 20;
             console.log(n); // 10
          }
          console.log(m); // m is not defined
          console.log(n); // 5
        }

上面代码中,表明在不同的代码块内的变量不会相互影响,同时外层的代码块也无法访问内层的代码块的变量,不同代码块可以用同名的变量。

2、不存在变量提升

    // var 的情况
    console.log(a); // 输出undefined
    var a= 2;

    // let 的情况
    console.log(b); // 报错ReferenceError,初始化之前无法访问'b'
    let b= 2;

上面代码中,变量a用var命令声明,会发生变量提升,即脚本开始运行时,变量a已经存在了,但是没有值,所以会输出undefined。变量b用let命令声明,不会发生变量提升。这表示在声明它之前,变量b是不存在的,这时如果用到它,就会抛出一个错误。

3、位于函数或代码顶部的var声明会给全局对象新增属性, 而let不会

    var a = 123;
    let b = 321;
    console.log(this.a); // 123
    console.log(this.b); // undefined

原因:全局环境记录在逻辑上是单个记录,但它被指定为封装对象环境记录和声明性环境记录的组合。对象环境记录的关联领域的全局对象的基础对象。此全局对象是全局环境记录的GetThisBinding具体方法返回的值。全局环境记录的对象环境记录组件包含所有内置全局变量的绑定(第18节)以及全局代码中包含的FunctionDeclaration,GeneratorDeclaration或VariableStatement引入的所有绑定。全局代码中所有其他ECMAScript声明的绑定包含在全局环境记录的声明性环境记录组件中。(网上看到的,但是没太懂

4、不允许重复声明

    // 报错,变量已被声明
    function () {
      let a = 10;
      var a = 1;
    }

    // 报错
    function () {
      let a = 10;
      let a = 1;
    }


    function func(arg) {
        let arg; // 报错,参数重复声明同
    }

    function func(arg) {
      {
        let arg; // 不报错
      }
    }

let不允许在相同作用域内,重复声明同一个变量。因此,不能在函数内部重新声明参数。

5、暂存死区

与通过 var 声明的有初始化值 undefined 的变量不同,通过 let 声明的变量直到它们的定义被执行时才初始化。在变量初始化前访问该变量会导致 ReferenceError。该变量处在一个自块顶部到初始化处理的“暂存死区”中

    console.log(a); // undefined
    console.log(b); // 报错,初始化之前无法访问
    var a= 1;
    let b= 2;

6、暂存死区与typeof

     typeof c // "undefined"
     typeof b; //undefined
     typeof a; // 报错,初始化之前无法访问
        let a;
        var b;

如上面的代码所述,一个用var声明的变量和一个未声明的变量,通过typeof返回的结果都是一个undefined,而使用typeof检测在暂存死区中的变量, 会抛出ReferenceError异常。这样的设计是为了让大家养成良好的编程习惯,变量一定要在声明之后使用,否则就报错。

注意

function bar(x = y, y = 2) {
     return [x, y];
}
    bar(); // 报错

上面代码中,调用bar函数之所以报错(某些实现可能不报错),是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于”死区“。如果y的默认值是x,就不会报错,因为此时x已经声明了。

    function bar(x = 2, y = x) {
      return [x, y];
    }
        bar(); // [2, 2]

    另外,下面的代码也会报错,与var的行为不同。
    在变量x的声明语句还没有执行完成前,就去取x的值,导致报错X未定义

    // 不报错
    var x = x;

    // 报错
    let x = x;
    // ReferenceError: x is not defined

const命令

1、基本用法

    // 定义常量MY_FAV并赋值7
    const MY_FAV = 7;

    // 报错 - Uncaught TypeError: Assignment to constant variable.
    MY_FAV = 20;

    // MY_FAV is 7
    console.log('my favorite number is: ' + MY_FAV);

    // 尝试重新声明会报错
    // Uncaught SyntaxError: Identifier 'MY_FAV' has already been declared
    const MY_FAV = 20;

    // MY_FAV 保留给上面的常量,这个操作会失败,字符'MY_FAV'已被声明
    var MY_FAV = 20;

    // 也会报错
    let MY_FAV = 20;

const声明一个只读的常量,但常量的值是无法(通过重新赋值)改变的,也不能被重新声明;常量在声明的时候可以使用大小写,但通常情况下全部用大写字母。

const MY_FAV;// SyntaxError: Missing initializer in const declaration

const一旦声明变量,就必须立即初始化,不能留到以后赋值,否则会报错

    if (true) {
      const MAX = 5;
    }
    MAX // Uncaught ReferenceError: MAX is not defined

const的作用域与let命令相同:只在声明所在的块级作用域内有效。

    if (true) {
        console.log(MAX); // ReferenceError
        const MAX = 5;
    }

const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。

2、常量可以定义成对象和数组

本质:

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

    const foo = {};
    // 为 foo 添加一个属性,可以成功
    foo.prop = 123;
    foo.prop // 123

    // 将 foo 指向另一个对象,就会报错
    foo = {}; // TypeError: "foo" is read-only

上面代码中,常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。

const a = [];
a.push('Hello'); // 可执行
a.length = 0;    // 可执行
a = ['Dave'];    // 报错

上面代码中,常量a是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给a,就会报错。

总结

let命令总结

  • 声明的变量仅仅在块级作用域有效
    块级作用域:外层作用于无法读取内层作用于周中的变量,内层作用域可以定义外层作用于中的同名变量
  • 不存在变量提升
  • 暂时性死区
    只要在块级作用域内存在let命令,他所生声明的变量就“绑定”这个区域,不在受外部影响。
  • 位于函数或代码顶部的var声明会给全局对象新增属性, 而let不会
  • 不允许重复声明

const命令总结

声明基本类型:

  • 一旦声明,值不可改变;
  • 声明的时候必须赋值,否则报错;
  • 只在块级作用域内有效
  • 存在暂时性死区
  • 不可重复声明

声明对象:

  • 变量名指向数据所在的地址,但是只保证变量名指向的地址不变。
  • 不可以重新赋值{不能让变量名指向另一个地址};
  • 对象本身是可变的,依然可以添加新的属性。

好了,let和const命令就讲到这里了,喜欢的小伙伴可以动动手指点点赞,让小作者有更大的动力去分享更多的知识给大家,谢谢!

你可能感兴趣的:(带你了解ES6 let 和 const 命令,建议收藏)