• 认识一下let和const


    let

    let不存在变量提升

    老规矩,先看代码

     1 console.log(a) //undefined
     2 let lock = false
     3 
     4 if (lock) {
     5     var a = 10
     6 }else{
     7     var a =20
     8 }
     9 
    10 console.log(a) //20

    ES5中只有全局作用域和函数作用域,因此var变量声明被提升到了全局作用域,ES6有了块级作用域,let 声明的变量只能在声明的作用域以及其子作用域使用,而var依然只有全局和函数作用域之分。再看下面代码。

    1 console.log(a) // 报错:a is not undefined
    2 let lock = false
    3 
    4 if (lock) {
    5     let a = 10
    6 }else{
    7     let a =20
    8 }

    a定义在if的block scope(块级作用域)里,let不存在变量提升,全局作用域无法使用

    注意下面这些写法会报错 

    1 let lock = false
    2 
    3 if (lock)  let a = 10  //报错: Lexical declaration cannot appear in a single-statement context
    4 else let a =20
    5 
    6 
    7 console.log(a) 
    1 let a = 2
    2 switch(a){
    3     case 1:let b = 20;break;
    4     case 2:let b = 30;break; //Identifier 'b' has already been declared
    5 }

      

    暂存死区(TDZ)

    look代码

    1 {
    2     console.log(b)  //报错:Cannot access 'b' before initialization
    3     typeof(b) //报错同上
    4     let b
    5 }

    在let b所属的作用域内,从作用域顶部到let b声明语句之前都存在暂存死区,此时对b进行任何有关的操作均会报错,这就是暂存死区。在全局作用域和函数作用域,let声明的变量都存在暂存死区现象。

    const声明的变量也存在暂存死区。

    注意事项

    同一作用域内let声明的变量不允许重复声明

    1 {    
    2     let b 
    3     let b  //报错:Identifier 'b' has already been declared
    4 }

    外层作用域无法访问内层作用域中let声明的变量,但内层可以访问到外层let声明变量。内外层声明互不干扰。自己可以动手谢谢看。

    常见例子

     1 for (var i = 0; i < 5; i++) {
     2     setTimeout(function() {
     3         console.log(i);
     4     }, 1000);
     5 }
     6 //这个例子执行完输出什么结果?
     7 
     8 //答案是 5,5,5,5,5
     9 
    10 //如果想要输出0,1,2,3,4改怎么实现?
    11 //其中一种方案就是把var换成let就可以了。let的块级作用域在这里就体现出来了。
    12 
    13 for (let i = 0; i < 5; i++) {
    14     setTimeout(function() {
    15         console.log(i);
    16     }, 1000);
    17 }

    为什么下面的代码可以实现需求所需要的输出?

    这就需要用到函数预编译相关知识了。函数的预编译在函数执行前一刻才会进行。这时候函数会把参数传入函数内部。对上面含var的for循环而言,当函数调用时也就是1s后,这时for已经执行完,函数预编译传入参数i。var声明的变量没有块级作用域概念,  仅有全局和函数作用域,故函数预编译寻找参数i的值时,找到的都是在全局作用域中的i,即都为5。

    对含let的for循环,循环每进行一次就声明一个i,这个i仅在那次循环的块作用域中,因此当函数执行时,预编译找到的参数i值仍是当时那次循环体内的对应的那个i值。

    在ES6以前,还有一种方法可以实现这种需求。

    1 for (var i = 0; i < 5; i++) {
    2        (function (i) {
    3            setTimeout(function () {
    4                console.log(i);
    5            }, 1000);
    6        })(i);
    7    }

    这段代码和let那段for循环都可以实现一样的输出,为什么?

    前面说了,函数预编译在函数执行前一刻进行,这里使用立即执行函数(IIFE),当函数定义完立即执行,此时循环还未执行完,i就已经被当作参数传入外面那层function,当一秒后内存function执行时,函数预编译在上一层函数作用域(和var的全局作用域找到的i不同)找到了那一次循环时的i值,因此效果和let一样,但原理上并不完全相同。

    参考babel对ES6中let转换成ES5的方法也印证了我上面的说法。

    关于函数预编译和立即执行函数相关知识,可参考下面两个链接。

    https://zhuanlan.zhihu.com/p/50236805

    https://www.cnblogs.com/L-xmin/p/11178599.html

    说完了let,说说const吧

    let具有的特征const都具有,比如不可重复声明和暂存死区等。下面说说const特有的

    const声明时必须赋值,且该值只读,不可修改。emmm,对object和array有点不一样。

    对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须非常小心。

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

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

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

    如果要做到对象完全不可更改,可以使用Object.freeze(),参考:https://segmentfault.com/a/1190000019348510

    还有点破事。。。

    前面讲var的时候博主讲过,对变量提升一词并没有准确的定义,而现在流行的看法也不见得完美无缺。

    众所周知let不存在变量提升,事实真的是这样吗?来看看下面这张图。

    注意在图1中,代码执行到var i = 0时,c的值在chrome的监控中是not available。但执行到块作用域内的var a = 20时候,c为undefined。如果按照之前var的说法,变量提升是把变量声明提升到作用域顶部,

    并初始化为undefined,那在这里的块作用域let是不是也存在所谓的变量提升?然let与var不同,即便c为undefined,但你在let c语句前对c的任何操作浏览器均会报错,因为暂存死区。如果按照另一种说法,变量提升的判断标志是能否在声明前使用,那么let是不存在变量提升的。注意,上面的情况只有块作用域有,全局作用域的let没有这种现象。这也许与chrome内部的机制有关,由于博主能力有限,并无法深究下去,有想法的小伙伴可以给我私信哦。但这不影响我们使用let,所以,以后尽量使用let和const声明变量而不是var。

     

  • 相关阅读:
    docker 命令(我使用过的)
    docker 概述
    RabbitMq 概述
    spring cloud gateway
    redis-集群(codis和Cluster)
    jdbc 简单示例和优缺点
    git
    Mycat--概述
    线程池
    同步容器和并发容器
  • 原文地址:https://www.cnblogs.com/AwenJS/p/12390377.html
Copyright © 2020-2023  润新知