美文网首页前端码无界前端前端学习
图例详解那道setTimeout与循环闭包的经典面试题

图例详解那道setTimeout与循环闭包的经典面试题

作者: 这波能反杀 | 来源:发表于2017-02-28 00:51 被阅读16526次

由于某些原因,文章已经删除,打算迁移到别处,目前正在寻找更合适的平台。

请大家关注我的新公众号ar_indus,随后我会在公众号里推送新的博客地址。

后续计划的《react进阶系列》文章也会在新公众号中推送。

公众号二维码

ar_indus

相关文章

  • 文摘-20170305

    前端 释义图例详解那道setTimeout与循环闭包的经典面试题js中proto和prototype的区别和关系?...

  • 图例详解那道setTimeout与循环闭包的经典面试题

    由于某些原因,文章已经删除,打算迁移到别处,目前正在寻找更合适的平台。 请大家关注我的新公众号ar_indus,随...

  • js闭包问题

    javascript 闭包的概念,闭包的作用,闭包经典面试题详解(配图解) 函数作用域(闭包前置知识) 要彻底弄懂...

  • setTimeout函数之循环和闭包

    setTimeout函数之循环和闭包 前言 之前对于setTimeout的一个经典问题的理解总是感到很迷惑,现在好...

  • 【JS】总算能把闭包经典面试题真的讲清楚了

    闭包的经典面试题 说到闭包,那么这个经典的闭包面试题,大家肯定都很熟悉: 这个例子当中为什么i输出的都是5呢?能够...

  • 闭包与setTimeout

    闭包 闭包的作用: 闭包的本质是一个函数闭包可以访问函数内部变量闭包的存在会使内部变量保留在内存中闭包的应用: 模...

  • 闭包1(基础)

    (什么是闭包?闭包的作用?闭包的缺陷?) (闭包的几种可能的应用场景) (闭包与内存泄漏,有关闭包的面试题) 推荐...

  • 闭包、setTimeout

    1.什么是闭包,闭包的作用是什么? 变量的作用域包括全局变量和局部变量。Javascript语言中,函数内部可以直...

  • Python闭包

    闭包 = 环境变量 + 函数 调用闭包内部的环境变量 闭包的经典误区 闭包与非闭包实现人类走路 非闭包 闭包

  • for循环与setTimeout、事件绑定

    闭包 闭包 = 函数 + 创建该函数的环境 问题 这些问题出现的原因,闭包不是主因,是由于setTimeout和事...

网友评论

  • 冷光啊啊啊:波老师,什么时候可以看啊。。。
    这波能反杀:@冷光啊啊啊 我马上弄
  • 7e1494e2eff1:用立即执行函数就可以解决了
  • 7661739843c6:为什么是恢复中:fearful:
  • Lazysunshi_ff25:博主你好,很喜欢你写的 前端进阶系列,其中有一篇https://www.jianshu.com/p/9b4a54a98660
    是恢复中状态,请问怎么才能访问呢
  • 尘埃落定_Y:利用最后提到的 私有化,就解决了。 把time 函数拿出来 在 异步里面 调 5次 每次传入 i ,i 在time 的作用域里面被私有化了。 每次执行流 改变了 i ,也不会被影响。
    尘埃落定_Y:给自己点赞的人 都很帅
  • 记录生活记录bug:for (var i=1; i<=5; i++) {
    setTimeout( function timer() {
    console.log(i);
    }, i*1000 );
    }直接讲var给成let就解决了
  • a2d7b36d630e:所以关键还是因为在 for循环中就 创建了5个独立的闭包函数, 每循环一次就创建了一个闭包函数, 每次循环的闭包函数其实都是独立的。
  • a2d7b36d630e:创建了5个闭包, 每次传给闭包的值不一样, i的每次变化并不影响上次闭包 value的值。
  • a2d7b36d630e:i虽然变化了, 但是value其实跟i没什么关系, 在for循环中value就确定了了值, 并且保存了起来;
  • a2d7b36d630e:for (var i=1; i<=5; i++) {

    (function(j) {
    var value = i;
    setTimeout( function timer() {
    console.log(value);
    }, i*1000 );
    })(i)
    }
  • a2d7b36d630e:好像看懂了, 没有闭包前, 所有console.log引用的是全局变量 i, 而i在console.log执行前的值就被改变了; 引入自执行函数闭包后, 只是把i的值传给闭包, 分别创建了5个闭包
  • 路某人_lu:想问大佬两个问题:1中间那个长代码快的输出第一行为什么是undefined而不是20;2.最后一个代码块,不是说setTimeout内的函数会在for循环之后执行吗,那为什么匿名函数绑定的i值不i是6,而是每个i.谢谢啦,小白问题多。。
  • mmmage:大神,fn.toString = function(){ return 30}这一行是啥意思?为什么执行后下边console.log(fn)就变成30了:disappointed_relieved:
  • af4093f71a34:大神,恕我无知
    fn.toString = function(){
    return 30;
    };
    .toString是啥意思?事件吗?把函数变成字符串?
  • f242b658719a:fn.toString = function() {
    return 30;
    }
    这一句会对代码有什么影响不懂,求大神解释一下啊
    这波能反杀:@有梦想的咸鱼_5207 我记得某一篇文章里有详细解释
  • eabae0900027:当i无限大的时候不会爆栈吗?队列里会有n个settimeout,所以建议用async,await或者co配合generator,或者递归尾调用
  • b7bfb8ee19a4:波波老师:这句话不太懂[将i值保存在一个闭包中,当setTimeout中定义的操作执行时,则访问对应闭包保存的i值即可。]
    for循环里定义了5个setTimeout的闭包。而当这些操作开始执行时,for循环的i值难道不是已经先一步变成了6了么。所以将i值保存在闭包里面的不也是6么
    for (var i=1; i<=5; i++) {
    setTimeout( (function(i) {
    return function() {
    console.log(i);
    }
    })(i), i*1000 );
    }
    这波能反杀:@Wrigley 创建了5个闭包,一个闭包保存一个值
  • 小太阳_9ae2:setTimeou的内容t等待到函数调用栈清空之后才开始执行是因为它是异步的吗?
  • a87b8111d5e1:直接console.log(i)不就完事了吗
  • 5567c7396dc4:看了一下您的文章 又去看了下高程 一些闭包的应用比如在for (var i=1; i<=5; i++) { alert(i)}中访问i每次都是5 按照高程理解了一下是不是因为js在ES5中没有块级作用域所以要模仿出一个类似块级作用域 所以要在外面定义一个自执行的匿名函数。
    这波能反杀:@Iaminjinan 闭包的理解,要从内存管理那里开始,不然总会有点想不明白的。模仿块级作用域是闭包的一个应用。而不是理解闭包要结合块级作用域来。
    5567c7396dc4:@波同学
  • SunnyCheung:保存在内存的i值不会被修改掉吗,i=1,i=2,i=3.....
  • 7cd363b098c9:请问 `队列:先进先出` 这张图片使用什么软件做的?? O(∩_∩)O谢谢
    Promise__: @Willard 应该是progress。。楼主的前端进阶前几篇里的评论里有
  • Gingbery:写的很棒,但是有一点不太理解,例子中的延迟时间为i*1000,但是观察到的延迟时间都是1s,请问是为什么?
    Promise__: @Gingbery 因为打印第一个值是1秒后,打印第二个值是2秒后,打印第三个值是3秒后,相对于前一个值,每个打印出来的值都间隔为1秒
  • 一缕殇流化隐半边冰霜:setTimeout 最后给的那个例子的答案好像不对,那个b变量会提升,所以会输出20,文章中给的答案是输出了undefined
    这波能反杀:@一缕殇流化隐半边冰霜 那我就不知道你说的是哪个例子了 ~ 这篇文章只有一个关于b的例子,输出是没错的
    一缕殇流化隐半边冰霜:@波同学 我把你的代码直接粘到Chrome的console里面打印出来的。。。b的20是可以打印出来的
    这波能反杀:我给出的答案应该是没错的,要不你写个demo试一下?
  • 罗彬727:for( var i=0;i<=5;i++){
    setTimeout(clock(i),i*1000);
    function clock(i){
    console.log(i)
    }
    } ,这样也可以
  • 12cb6232f15c:有一个疑问:
    “ 而这个队列执行的时间,需要等待到函数调用栈清空之后才开始执行。”
    函数调用栈应该很没有算清空吧,不是还有栈底的全局上下文吗?
    Promise__: @12cb6232f15c http://ghmagical.com/article/page/id/H61NOVU0RZ9Y建议看这篇文章
  • de96d6727049:写的非常好,评论里有用的地方也很多,不过我有个地方的认识和你不同:“我们就必须借助闭包的特性,每次循环时,将i值保存在一个闭包中”,我认为是再利用IIFE运行会产生块级作用域(类似let)的方式保存i,并不是闭包的特性,这个题目中用到闭包,完全是因为如果只是立即执行函数,返回的不是一个函数的话,不会按照时间间隔输出,而是立即执行,然后输出,比如我把你的方法二中的闭包部分删掉:
    for (var i=1; i<=5; i++) {
    setTimeout( (function(i) {
    //return function() {
    console.log(i);
    //}
    })(i), i*1000 );
    }
    代码的i值还是可以访问到(说明访问到i的正确值不是闭包的关系),但是不能隔一秒输出一次,而是所有的console被立即执行了。
    所以我认为您上述给出的两种解决方法都很好,但是闭包和IIFE在其中起到的作用我不是很认同。如果我的认识有不对的地方,欢迎纠正我的认识。:blush:
    de96d6727049:@波同学 在你这篇文章的评论里我看到有人使用settimeout的第三个参数的方法,然后用你的方法在chorme中调试发现,不同于使用闭包的情况,使用闭包时scope中依次是local-closure-global,执行时i的值放在closure中;而使用第三参数传入i时,scope中依次是local-global,i的值存放在local中,没有使用闭包,很好奇是怎么实现的,查找了半天没有找到,大神知道么?
    de96d6727049:@波同学 多谢你的提醒,我下来做了几个实验,发现确实是我之前的理解一直有矛盾的地方,确实是保存在闭包中。立即执行函数只是将i调用时的值传入闭包保存,形式上完全可以不用立即执行函数的方式,直接函数调用。核心点还是闭包。
    这波能反杀:@该用户还没想好叫什么 你理解肯定是有一些问题的。你对闭包的了解应该有点不到位,建议再思考一下:i值是如何保存。想想你的例子里为什么i值没有保存,以及你的例子里输出的12345到底是什么,与保存在闭包里的i值有什么区别
  • 0bf8ee86a331:波老师,能解释下这个变种的原理吗?
    for (var i = 1; i <= 5; i++) {
    setTimeout((function(i) {
    console.log(i);
    })(i), i * 1000);
    }
    这波能反杀:@evanoxu 你对闭包了解太浅了,另外几篇文章去看看
    0bf8ee86a331:@波同学 我也看不懂原理。如果按照执行,是没有setTimeout的作用,直接循环输出的。求分析…
    这波能反杀:@evanoxu 这是什么变种?
  • 6536e0a782f4:火狐、360、谷歌浏览器的执行结果怎么还不一样呢?
    火狐:
    undefined
    function fn()
    function fn()
    setTImeout 10ms.
    setTimeout 20ms.


    360:
    undefined
    fn() {
    setTimeout(function() {
    console.log('setTImeout 10ms.');
    }, 10);
    }
    function 30
    10
    setTImeout 10ms.
    setTimeout 20ms.

    谷歌:
    undefined
    function 30
    function 30
    10
    setTImeout 10ms.
    setTimeout 20ms.
    这波能反杀:@Amy_Angela 内部实现不一样
    不用去纠结具体的优先级
  • 6536e0a782f4:setTimeout(function() {
    console.log(a);
    }, 0);

    var a = 10;

    console.log(b);
    console.log(fn);

    var b = 20;

    function fn() {
    setTimeout(function() {
    console.log('setTImeout 10ms.');
    }, 10);
    }

    fn.toString = function() {
    return 30;
    }

    console.log(fn);

    setTimeout(function() {
    console.log('setTimeout 20ms.');
    }, 20);

    fn();


    请问这个例子中
    fn.toString = function() {
    return 30;
    }
    fn.toString在执行上下文创建的时候会被创建吗?还是说function变量 fn创建了.toString这个function变量也被创建了?另外setTimeout这个函数也会被创建吗?这个函数创建后保存这个函数的引用的变量名称是什么呢?
  • 10e5b44da6a6:你好,请教下
    function foo(){
    for(var i = 0 ; i<2 ; i++){
    setTimeout(function timer() {
    console.log(i) ;

    },i*1000);
    }
    }
    这个代码中,最后打印2 2,这个我能理解,我想问一下这里的 i*1000,我的理解是1*1000,2*1000.也就是说第一个2是间隔一秒钟之后打印的,第二个2是间隔二秒之后打印的。但是我看浏览器打印的间隔时间是一样的,这是怎么回事?请指教。谢谢
    useless1:@踏遍万里河山 怎么打印不出2了执行了可以打印出2啊
    10e5b44da6a6:@踏遍万里河山 大概懂了,谢谢,现在才看到
    ef2485d914fc:@词不达意_e6f5 首先,你的这段代码打印不出来2
    然后,出现时间间隔一样的原因是前一个的1s也包含在后一个的时间里。
  • bestvow:我会用let吧:smiley:
  • 一wei渡江:console.log(fn);
    function fn() {}
    fn.toString = function() {
    return 30;
    }
    console.log(fn);
    前面的fn为函数体, 后面的fn为30
    这里不太理解
    函数的toString属性和valueOf属性有什么关系
    同时重写fn. toString和fn. valueOf, 他们都会被调用, toString先调用valueOf后调用, 结果为valueOf的值.只重写toString, 这个函数会被调用两次. 只重写valueOf只会被调用一次.
    它们是fn. toString=fn. valueOf=function(){return 30}这样吗,指向同一个函数toString先调用valueOf后调用.
    fn直接回车输出内容的过程是什么
    一wei渡江:@波同学 谢谢,看了你的文章收获很大
    这波能反杀:@一wei渡江 我也不知道
  • 夏目祐太:function fn(i) {
    setTimeout(function() {
    console.log(i);
    }, 1000)
    }

    for(var i = 0; i < 5; i++) {
    fn(i);
    }

    突然想到也可以这么解决,简单粗暴
  • 6b5a2e2b487a: var name = "The Window";
      var object = {
        name : "My Object",
        getNameFunc : function(){
    alert(this.name);
          return function(){
            return this.name;
          };

        }
      };
    alert(object.getNameFunc()());
    useless1:@EvanChenug 调用过程中确定this,最后一次相当于直接window下调用啊
    6b5a2e2b487a:为什么
    6b5a2e2b487a:波老师,return this.name会指向window哇
  • Dream4ever:前几天在项目中刚好遇到了这个问题,百思不得其解,今天真是走运,看到了这篇文章,疑虑顿消,大谢!
  • bfc7f62eb494:你好,我运行出来的是第一个为 10 , function fn(),funtion 30 这个就不懂了,10,settimeout 10ms, settimeout 20ms;
    demo11:@老三不爱吃面 给个例子,我试试看
    demo11:@老三不爱吃面 怎么看出的console.log会隐式调用toString方法呢?
    useless1:@Admin_4f18 因为console.log会隐式调用toString方法,前面重新定义了toString方法啊:relieved:
  • darayo::joy: 好像懂了~
  • 763bf7c17310:点个攒 , settimeout:clap:
  • a58b0bc9e87e:w3school的级别:
    for(var i=0;i<5;i++){
    setTimeout("console.log("+i+")",i*1000);
    }
    hkcmd:没看懂?
  • 239f01adc9eb:支持支持1!!
  • 始悔不悟:谢谢,很有帮助
    这波能反杀:@始悔不悟 有帮助就好
  • 析子:虽然我正在讲高中课本里的算法语句,可是我还是看不懂这么高深的学问:disappointed_relieved:,佩服你,加油哦,㊗️你早日签约:blush:
  • 柒淡墨:很棒
  • 风萧萧梦潇:另外几种方式:
    1.利用setTimeout第三个参数
    for (var i=1; i<=5; i++) {
    setTimeout( function timer(i) {
    console.log(i);
    }, i*1000,i );
    }
    2.利用bind方法
    for (var i=1; i<=5; i++) {
    setTimeout( function timer(i) {
    console.log(i);
    }.bind(null,i), i*1000 );
    }
    3.利用let
    for (let i=1; i<=5; i++) {
    setTimeout( function timer() {
    console.log(i);
    }, i*1000 );
    }
    279bae7e32be:bind(null)是什么意思啊?不太理解 求解惑
    0c09725b07dc: @风萧萧梦潇 👏👍
    微醺岁月:for (var i = 0; i < 5; i++) {
    setTimeout(console.log.bind(console,i), 1000 * i)
    }
  • 744bb97e6c1a:大神忘记了es6的let:smile:
    尤小小:@波同学 😁
  • 56e26a4f693b:感谢。看到这熟悉的例子真亲切,之前看书的时候看着看着就晕了,经LZ这么一解释现在虽然还是有点迷糊但好歹有点理解了:dizzy_face:
  • 朵朵鱼:佩服,把操作变成一篇文章肯定要花好多时间👍👍
    这波能反杀:嗯,确实花时间 - -
  • WangChloe:波同学,请问setTimeout传入第三个参数i,同时里面的方法接收i出现的结果是什么原理,一直没找到这个问题的答案,是在一次笔试中看到的题目
    WangChloe:@风萧萧梦潇 好的,谢谢,我去看看
    风萧萧梦潇:第三个参数是传给匿名函数的参数。这篇文章写的挺好http://blog.csdn.net/fightingboy8888/article/details/54311156
  • JohnsonChe:写的很好
  • 669ab63fb60b:for (var i=1; i<=5; i++) {
    (function(b){
    return setTimeout( function timer() {
    console.log(b);
    }, b*1000 )
    })(i)
    }
  • 无戒:大神。厉害
  • 淘淘笙悦:写的很详细,实在感谢

本文标题:图例详解那道setTimeout与循环闭包的经典面试题

本文链接:https://www.haomeiwen.com/subject/neptgttx.html