• 01.JS块级作用域与let


    1.块级作用域
       什么是:
             在一个代码块(括在一对花括号中的一组语句)中定义的所需变量(与let配合使用)并在代码块的外部是不可见的。
       为什么:
             在ES6之前,函数只能在全局作用域和函数作用域中声明,不能在块级作用域中声明,造成诸多问题:
                  1.内层变量会覆盖外层变量
                  2.用来计数的循环变量泄漏变成全局变量
       何时:
             需要强化对变量生命周期的控制,避免全局污染出现错误
       优点:
             1.阻止变量泄漏变为全局变量,造成全局污染
             2.阻止变量提升覆盖
             3.内层作用域可以定义与外层作用域相同的变量名,但变量是独立的。
             4.允许任意嵌套,外层作用域无法读取内层作用域的变量
       块级作用域:
             {},if{},else{},while{},for{},swicth{}…

        //块级作用域{},与let配合使用
        {
          //大括号之间就是块级作用域
          var num=1;//还是全局变量,可以被外部访问
          let num=1;//局部变量,不可以被外部访问,块级作用域必须用let声明
        }
        console.log(num);//此时num已经不可以被访问,说明块级作用域生效
    
        //内层作用域可以定义与外层作用域相同的变量名,允许任意嵌套
        //但变量与变量之间是没有关系的,都是独立的个体。
        {
          let aa=1;
          console.log(aa);//1
          {
            let aa=11;
            console.log(aa);//11
            {
              let aa=111;
              console.log(aa);//111
            }
          }
        }
        console.log(aa);//此时是不能被访问的,防止aa泄露为全局变量
    
        //防止变量提升造成覆盖
        //虽然都是f,但是配合了let使用,{}内部变成了一个小个体,不会影响其他的f
          var f=17;
          {
            let f=28;
            console.log(f);
          }
          console.log(f);
    
        //for块级作用域
        //计算1-100之间所有的整数的和,使用var声明
        for (var i=1,sum=0;i<=100;i++){
          sum+=i;
        }
        console.log(sum);//5050,可以访问
        console.log(i);//101,可以访问
    
        //使用let声明变量
        for (let i=1,sum=0;i<=100;i++){
          sum+=i;
        }
        console.log(i);//此时i是不能被访问的。
        console.log(sum);//此时sum是不能被访问的。
        //根据需求得知,最后需要访问的是sum,i需要释放
        //所以把sum单独放在外面,sum就可以被访问,而i会被释放
        var sum=0;
        for (let i=1;i<=100;i++){
          sum+=i;
        }
        console.log(sum);

    2.let声明
       什么是:
             专门代替var来声明变量用的
       为什么:
             var的问题:
                 1.声明提前
                 2.没有块级作用域
                 3.造成全局污染
       何时:
             只要声明变量都用let
       优点:
             1.阻止了声明提前
             2.添加了块级作用域
             3.成为局部变量,不会造成全局污染
       原理:
             let其实就是一个匿名函数自调用!
             且let为了双保险,其实在底层悄悄给变量改了名字,在变量前增加了_
       let的小脾气:
             1.在相同作用域/块内:禁止同时let两个同名的变量
             2.在let 变量 之前到当前作用域的顶部之间,不允许提前使用变量
             3.不可以在函数内部重复声明参数

        //1.简单了解let声明的变量
        let a=2;
        console.log(a);
        let a=3;
        console.log(a);//报错,a不能重复声明并赋值
    
        //var声明的变量可以重复声明并重新赋值
        var b=2;
        console.log(b);//2
        var b=3;
        console.log(b);//3
    
        //在块级作用域内{}
        {
          var c=2;
          let c=4;//在同一块级作用域内不允许重复声明变量
          let d=3;
          console.log(d);//只能在当前{}块级作用域内访问
        }
        console.log(c);//可以被外部访问的是var声明的c
        console.log(d);//不可以被外部访问
    
        //let不允许先调用,后声明
        {
          console.log(aaa);
          let aaa=5;//报错Cannot access 'aaa' before initialization
        }

             把let放入实例中理解

            //let应用
            //累加每个任务函数的时间
            var t=0;
            function task1(){
                console.log(`任务1耗时3s`);
                t+=0.3;
            }
            function task2(){
                console.log(`任务二耗时8s`);
                t+=0.8;
            }
            task1();
            task2();
            console.log(`共耗时${t}s`)
             以上是一个可以正常执行的代码,并且是正确的程序
             如果在task2内添加其他的功能,例如捕捉错误
            //累加每个任务函数的时间
            var t=0;//声明变量t准备累加每个任务函数的时间
            function task1(){
                console.log(`任务1耗时3s`);
                t+=3;
            }
            function task2(){
                console.log(`任务二耗时8s`);
                t+=8;
                //模拟出错的变量,此段代码是不执行的
                var err=false;
                //如果出错
                if(err==true){
                    //就获得出错时间
                    var t=new Date();
                    //并输出出错提示日志
                    console.log(`出错啦,at:${t.toLocaleDateString()}`);
                };
            }
            task1();
            task2();
            console.log(`共耗时${t}s`)
             代码正常执行,if内代码是不执行的,但是结果却是错误的,少了8s,那么为什么少了8s呢?
             原来是因为var声明的变量是全局变量,而if{}不是作用域,没有实体墙,拦不住var,
             所以var声明的变量会提升到当前作用域task2的最前面。
            //累加每个任务函数的时间
            var t=0;//全局t
            function task1(){
                console.log(`任务1耗时3s`);
                t+=3;
            }
            function task2(){
                //var t;//undefined,var声明的t提前到该作用域最前面,并且没有赋值
                //如果task2()中已经有了局部变量t,就不会用全局的t了,只有在局部没有的t的时候才会调用全局的t
                console.log(`任务二耗时8s`);
                //这个8s没有加到全局t,而是加在局部t上,当函数调用后,局部的t就被释放了
                t+=8;//task2中的局部变量t,加在这里
                var err=false;//模拟出错的变量
                //如果出错
                if(err==true){//if else for while do whlie 等程序结构的{}不是作用域,不是实体墙,拦不住var
                    //就获得出错时间
                    var t=new Date();
                    //并输出出错提示日志
                    console.log(`出错啦,at:${t.toLocaleDateString()}`);
                };
            }
            task1();
            task2();
            console.log(`共耗时${t}s`)//此处输出的是全局的t,所以没有那8s,
             那么鉴于这种明明没有执行的代码,却破坏了原本正确的代码,
             这就是没有块级作用域带来的危害,此时就需要使用let声明变量了,
             因为let会阻止声明提前,并会添加块级作用域
           //累加每个任务函数的时间
            var t=0;//全局t
            function task1(){
                console.log(`任务1耗时3s`);
                t+=3;
            }
            function task2(){
                console.log(`任务二耗时8s`);
                t+=8;//这个t还是会加到全局的t上,没有被影响
                //模拟出错的变量
                //var err=false;//代码不执行
                var err=true;//代码执行
                //如果出错
                if(err==true){//let将if{}也变成了一级作用域,这个作用域是有实体墙的,是可以拦住let声明的变量的                //就获得出错时间
                    //let阻止了局部的t被声明提前
                    let t=new Date();//此时的t是在这个if块级作用域的函数内,不会存在提升
                    //并输出出错提示日志
                    console.log(`出错啦,at:${t.toLocaleDateString()}`);//所以此时输出的t也是if内的t
                };
            }
            task1();
            task2();
            console.log(`共耗时${t}s`)//打印全局t
             let的原理,添加匿名函数自调用,并改变名字
            //累加每个任务函数的时间
            var t=0;//全局t
            function task1(){
                console.log(`任务1耗时3s`);
                t+=3;
            }
            function task2(){
                console.log(`任务二耗时8s`);
                t+=8;//这个t还是会加到全局的t上,没有被影响
                //var err=false;
                var err=true;//模拟出错的变量
                //如果出错
                if(err==true){//let将if{}也变成了一级作用域,这个作用域是有实体墙的,是可以拦住let声明的变量的
                    //(function(){//let自动添加的
                    //就获得出错时间
                    //let阻止了局部的t被声明提前
                    let t=new Date();//let悄悄改变了名字,变成了_t,此时的t是在这个if块级作用域的函数内,不会存在提升到
                    //并输出出错提示日志
                    console.log(`出错啦,at:${t.toLocaleDateString()}`);//_t,所以此时输出的t也是if内的t
                    //})();//let自动加的
                };
            }
            task1();
            task2();
            console.log(`共耗时${t}s`)//打印全局t    

             单词:
                   declare——声明
                   access——访问
                   initialization——初始化——>第一次声明+赋值=初始化

  • 相关阅读:
    【图论】2-SAT 问题
    【网络流】费用流(基于Capacity Scaling)
    CF gym 102483(NWERC 2018) A题 解答
    【网络流】最小点权覆盖集、最大点权独立集
    【网络流】最大密度子图
    【网络流】最大权闭合图
    简易 vim 配置
    生成函数基础
    「NOI.AC」NOI挑战赛第二场
    SDOI2020 退役记
  • 原文地址:https://www.cnblogs.com/oksana/p/12386357.html
Copyright © 2020-2023  润新知