当前位置: 首页 > 科技观察

图例详解setTimeout和闭环经典面试题

时间:2023-03-14 23:41:16 科技观察

我在文末留下一道关于setTimeout和闭环的思考题详解作用域链和闭包。使用闭包修改以下代码,使循环的输出为1,2,3,4,5for(vari=1;i<=5;i++){setTimeout(functiontimer(){console.log(i);},i*1000);}令人欣慰的是,很多朋友看了文章后确实对闭包有了更深的理解,给出了几种准确的写法。有的朋友能够仔细阅读我的文章,并逐一举例练习。这种认可真的让我很感动。但是,也有一些基础不好的朋友,看完之后对这道题的理解还是一头雾水。因此,应一些读者的要求,我将通过这篇文章来分享关于setTimeout的知识。我希望每个人都能阅读它。有新的收获。刚开始学习setTimeout的时候,我们很容易知道setTimeout有两个参数,第一个参数是一个函数,我们通过这个函数来定义要执行的操作。第二个参数是一个毫秒数,表示延迟执行时间。setTimeout(function(){console.log('我会在一秒内打印出来')},1000)上面这个例子的执行结果可能会阻止很多人对setTimeout的理解,但是很多人还是会发现它还有一些其他的东西,并在评论中提问。比如上图中的数字7是什么?每次setTimeout执行的时候,都会返回一个***ID,上图中的数字7就是***ID。我们在使用的时候,经常会用一个变量来保存***ID,传给clearTimeout来清除定时器。vartimer=setTimeout(function(){console.log('如果你不清除我,我会在一秒后出现。');},1000)clearTimeout(timer);//清除后,setTimeout定义的操作是不是会被执行接下来,我们需要考虑另一个重要的问题,即setTimeout中定义的操作什么时候会被执行?为了引起大家的注意,我们来看下面这个例子。vartimer=setTimeout(function(){console.log('setTimeoutactions.');},0);console.log('otheractions.');//想想,当我把setTimeout的延迟时间设置为0时,上面的执行顺序是什么?试着在浏览器的控制台运行一下,你就很容易知道答案了。如果你没有猜到答案,那么我的文章值得你点赞,因为接下来我分享的小知识可能会在笔试中救你一命。在介绍执行上下文的时候,给大家分享了函数调用栈这种特殊数据结构的调用特点。这里,再介绍一种特殊的队列结构。页面上setTimeout定义的所有操作都会在同一个队列中依次执行。我用下图给大家展示队列数据结构的特点。队列:先进先出,这个队列的执行时间需要等到函数调用栈清空后才开始执行。也就是说,setTimeout定义的操作只有在所有可执行代码都执行完后才会开始。这些操作进入队列的顺序由设置的延迟时间决定。所以在上面的例子中,即使我们将延迟时间设置为0,它定义的操作仍然需要等待所有的代码执行完才开始执行。这里的延迟时间不是相对于执行setTimeout的时刻,而是相对于其他代码执行的时刻。所以上面这个例子的执行结果是非常容易理解的。为了帮助您理解,让我们举一个更复杂的变量提升示例。如果你能正确地看到执行顺序,那么你对函数的执行就有了更正确的理解。如果不能,请返回并阅读其他文章。setTimeout(function(){console.log(a);},0);vara=10;console.log(b);console.log(fn);varb=20;functionfn(){setTimeout(function(){console.log('setTImeout10ms.');},10);}fn.toString=function(){return30;}console.log(fn);setTimeout(function(){console.log('setTimeout20ms.');},20);fn();Shangli的执行结果是OK的,setTimeout这里暂且介绍一下。我们回过头来看看闭环的思考题。for(vari=1;i<=5;i++){setTimeout(functiontimer(){console.log(i);},i*1000);}如果我们直接这样写,setTimeout定义的操作会在functioncallstack清除后会执行的特性,for循环中定义了5次setTimeout操作。而当这些操作开始执行的时候,for循环的i值已经一步变为6了。所以输出总是6。而如果我们想要输出的结果是顺序执行的,就必须利用闭包的特性,在每次循环的时候将i的值保存在一个闭包中,当执行setTimeout中定义的操作时,我们将访问相应闭包保存的值i的值就足够了。而我们知道在一个函数中判断闭包的标准,就是在执行过程中是否在内部定义的函数中访问了上层作用域中的变量。因此,我们需要包裹一层自执行函数,为闭包的形成提供条件。因此,我们只需要两次操作就可以完成题目要求。一种是利用自执行函数提供闭包条件,另一种是传入i值保存在闭包中。for(vari=1;i<=5;i++){(function(i){setTimeout(functiontimer(){console.log(i);},i*1000;})(i)}使用断点调试,在chrome中,查看执行顺序和每个闭包中不同的i值。当然你也可以在setTimeout的第一个参数处使用闭包。for(vari=1;i<=5;i++){setTimeout((函数(i){returnfunction(){console.log(i);}})(i),i*1000);}