资讯专栏INFORMATION COLUMN

循环和闭包

Cc_2011 / 1105人阅读

摘要:所以所有函数共享一个的引用时,循环结构让我们误认为背后还有更复杂的机制在器作用,但实际上啥都木有,如果将延迟函数的回调重复定义五次,完全不使用循环,那他同这段代码是完全等价的。

想要说明闭包,for循环是最常见的例子:

for(var i=1;i<=5;i++)
{
    setTimeout(function timer(){
        console.log(i);
    },i*1000);
}

以我们所想,我们可能认为他会输出1~5,每秒一次,每次一个。
但实际上,这段代码在运行时会以每秒一次的频率输出五次6。

这是为什么?

原因是延迟函数会在循环结束时才执行,事实上,当定时器运行时即使每个迭代中执行的是setTimeout(...,0),所有的回调函数依然是在循环结束后才会执行,因此会每次输出一个6出来。

根据作用域的原理,实际情况:尽管循环中的五个函数是在各个迭代中分别定义的,但是他们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。
所以所有函数共享一个i的引用时,循环结构让我们误认为背后还有更复杂的机制在器作用,但实际上啥都木有,如果将延迟函数的回调重复定义五次,完全不使用循环,那他同这段代码是完全等价的。

解决方法如下:
我们先试一下:

    for(var i=1;i<5;i++){
        (function(){
            setTimeout(function timer(){
                console.log(i);
            },i*1000);
        })();
    }

看似可以,但实际也没用,虽然这样写我们有更多词法作用域了,的确每个延迟函数都会将IIFE在每次迭代中创建的作用域封闭起来。
如果作用域是空的,那么仅仅将他们进行封闭是不够的。仔细看一下,我们的IIFE只是一个什么都没有的空作用域,所以需要包含一点实际内容为我们所用。

他需要自己的变量,用来在每个迭代中存储i的值:

    for(var i=0;i<=5;i++)
    {
        (function(){
            var j=i;
            setTimeout(function timer(){
                console.log(j);
            },j*1000);
        })();
    }

ok,他运行如我们所愿了!

可以进行改进:

    for(var i=1;i<=5;i++)
    {
        (function{
            setTimeout(function timer(){
                console.log(j);
            },j*1000);
        })(i);    //i可以改动,只要你喜欢
    }

在迭代内使用IIFE会为每个迭代都生成一个新的作用域,使得延迟函数的回调可以将新的作用域封闭在每个迭代内部,每个迭代中都会包含一个具有正确值的变量供我们访问。

使用let解决

for循环的let声明还会有一个特殊行为,这个行为之处变量在循环过程中不知被声明一次,每次迭代都会声明,随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。

    for(var i=1;i<=5;i++)
    {
        let j=i;  //闭包
        setTimeout(function timer(){
            console.log(j);
        },j*1000);
    }
    
    下面是进化版
    
    for(let i;i<=5;i++)
    {
        setTimeout(function timer(){
            console.log(i);
        },i*1000);
    }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94919.html

相关文章

  • [Javascript实验课]循环中的闭包

    摘要:执行出来的结果是这样的实验发现,无论如何都在最后执行,这证实了我们之前遇到的问题,因为在循环结束才执行,所以回调函数调用的取值必然是循环的最后一次。 前言 https://developer.mozilla.org/zh-CN/docs/JavaScript/Guide/Closures MDN上描述闭包的章节阐述了一个由于闭包产生的常见错误,代码片段是这样的 for (var i...

    teren 评论0 收藏0
  • 前端小知识--从Javascript闭包看let

    摘要:闭包会在父函数外部,改变父函数内部变量的值。立即执行函数立即执行函数,顾名思义,立即会执行的函数,即当读取到该函数,会立即执行。特性使用语句声明一个变量,该变量的范围限于声明它的块中。使用声明的变量,在声明前无法使用,否则将会导致错误。 let和闭包 之前一直模模糊糊记得,let解决了某个闭包问题,想用时又不敢肯定,今天终于遇到这个问题了,那我们就一起来分析一下,什么是let,let有...

    Kross 评论0 收藏0
  • JavaScript中的闭包

    摘要:权威指南第版中闭包的定义函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中成为闭包。循环中的闭包使用闭包时一种常见的错误情况是循环中的闭包,很多初学者都遇到了这个问题。 闭包简介 闭包是JavaScript的重要特性,那么什么是闭包? 《JavaScript高级程序设计(第3版)》中闭包的定义: 闭包就是指有权访问另一个函数中的变...

    Donne 评论0 收藏0
  • JS 基础篇--闭包引用

    摘要:因为没有块级作用域,只有函数作用域,所以闭包的使用与函数是紧密相关的。模拟私有变量这里返回两个闭包函数和。闭包会在父函数外部,改变父函数内部变量的值。 简介 Javascript 中一个最重要的特性就是闭包的使用。因为闭包的使用,当前作用域总可以访问外部的作用域。因为Javascript 没有块级作用域,只有函数作用域,所以闭包的使用与函数是紧密相关的。 各种专业文献上的闭包(clos...

    EdwardUp 评论0 收藏0
  • 深入理解JavaScript(二):由一道题来思考闭包

    摘要:中所有的事件绑定都是异步编程当前这件事件没有彻底完成,不再等待,继续执行下面的任务当绑定事件后,不需要等待执行,继续执行下一个循环任务,所以当我们点击执行方法的时候,循环早已结束即是最后。 概念 闭包就是指有权访问另一个函数作用域中的变量的函数 点击li标签弹出对应数字 0 1...

    曹金海 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<