最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 深度理解for循环的作用域

    正文概述 掘金(橘猫走江湖)   2021-01-19   419

    深度理解for循环中let的作用域

    1. let 的「创建」过程被提升了,但是初始化没有提升。
    2. var 的「创建」和「初始化」都被提升了。
    3. function 的「创建」「初始化」和「赋值」都被提升了。

    4、const 只有[创建][初始化]的过程,没有赋值过程

    1、var、let 与作用域


    代码一:

    for(var i = 0; i <5; i ++){ 
      setTimeout(()=> console.log(i)) 
    }
      
    // 输出为:5 5 5 5 5
    


    代码二:

    for(let i = 0; i <5; i ++){ 
      setTimeout(()=> console.log(i)) 
    }
      
    // 输出为:0 1 2 3 4
    

    解析for循环中var:

    关于这两段代码,大部分前端都能正确输出,但是对于为什么会这样输出却没有头绪。下面来一步步分析一下:
    我们可以先对代码一做一下修改:

    for(var i = 0; i <5; i ++){ 
      console.log('输出i为:' + i)
      setTimeout(()=> console.log(i)); 
    }
    

    查看输出结果为:
    深度理解for循环的作用域

    执行顺序是这样的:
    ①声明变量var i = 0,因为var声明的变量不存在块级作用域概念,i是一个全局变量。
    ②判断条件, i < 5,为true,继续
    ③执行console,输出“输出i为:0”,继续
    ④遇到宏任务定时器,我们知道这是一个异步语句,会进入异步队列。所以回调函数不会执行。
    继续执行, i ++
    ⑤执行步骤②...,知道判断条件为false
    ⑥i=5时,不符合条件,for语句执行完毕
    ⑦for循环是同步语句,执行完毕后,执行异步语句。也就是延迟期的回调。关于此处,有些人还是存在误区,按照这样的话,为什么会输出5个5呢,为啥不是输出一个5?
    解答:
    关于这个问题,我可以在上面代码中做一些变动,相信很多人就能理解了。

    var timer = null
    for(var i = 0; i <5; i ++){ 
      timer = setTimeout(()=> console.log(i)); 
      console.log('for循环内的:' + timer)
    }
    console.log('for循环外的:' + timer)
    


    输出如下:
    深度理解for循环的作用域

    从这里,我们可以看到延迟期是执行了5次,而每一次执行后,回调都会进入异步队列。也就是说,当for语句循环完毕后,我们有5个异步回调语句等待执行。

    ⑧执行延迟器的回调,执行console.log(i),此时回调的作用域是全局作用域为全局作用域(在let、const引入前,js只有全局作用域、函数作用域这两个概念),此时i=5,自然输出5个5

    解析for循环中let:


    首先明确一点:let声明会进入当前的块级作用域中('{}'所包裹的环境,就是当前块级作用域).
    对上面的代码稍做改动,将var变为let:

    var timer = null
    for(let i = 0; i <5; i ++){ 
      timer = setTimeout(()=> console.log(i)); 
      console.log('for循环内的:' + timer)
    }
    console.log('for循环外的:' + timer)
    

    此时输出为:
    深度理解for循环的作用域

    参照上面对于var的解析。let生命的for循环,不同之处其实就是其中异步语句回调,在执行时候作用域的不同。
    针对上面的输出图,我们一步步分析:
    从上面图中共我们可以看到,在回调执行的时候不同。为什么会这样呢。

    假设一:异步语句在执行前已经拿到了i的值
    验证:

    let i = 0;
    setTimeout(function() {
      console.log(i);
    }, 0);
    i++;
    // 输出为:1
    

    结论:假设不成立。
    推翻上面的假设后,只会存在一种可能,那就是5个异步语句,在执行时候,这五条console语句所处的作用域中的i的值就是不同的!也就是说着五个回调,处在五个不同的作用域中!!!
    当然,上面只是我们大胆论证的结论。可是为什么会这样的,之前对于let和for循环的理解到底哪里出了问题?
    如此神奇?继续探索!
    var 和let到底有什么不同?
    。。。

    根据上面的结论,我们先反推出在循环时候,对let做了什么:

    ①在执行**final-expression**的时候,不是执行 i = 2,而是重新生成了一个作用域,并let i = 2...
    ②for循环的每次循环体,形成了新的作用域,并将宏任务seTimeout放入异步队列的时候,将当前的作用域也做了记录。当setTimeout的函数执行的时候的作用域,就是与其同时的作用域。

    从这里看,for循环的执行过程,颠覆了我之前的认知。到底怎么回事。那么是只有let声明会这样,还是var声明也是这样(由于var是全局,与上面的结论不矛盾)。该如何验证。
    当然,这只是反推出的结论,可是该如何去验证,或者哪里有官方的文档描述过类似的过程呢。此时,我不禁又陷入思索。MDN、ES、知乎、掘金...

    搜索掘金,看到这样一篇文章:juejin.cn/post/684490…
    里面提到一个词,词法作用域,第一次听说,感觉可以解决我的问题。
    这里对for循环的执行过程做了阐释,每次执行_condition为true_都会创建一个新的词法作用域,并声明新的变量i。当异步语句执行的时候,查找的是当前此法作用域下的变量。

    结论:

    在for循环的判断阶段,会产生新的词法作用域(包括初始化的时候),并在作用域中声明新的变量i。
    但是由于var声明时候存在变量声明的提升,被提升到了词法作用域外。因此,var输出时候,寻找到的变量i是作用域外(已经变为5的i)。但是let的声明提升是在块级作用域中的。输出的会是当前词法作用域下的变量i







    下载网 » 深度理解for循环的作用域

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元