前端学堂 http://www.felearn.com
当前位置首页 > 前端技术 > JavaScript> 正文

JS中循环和闭包如何理解?

2021-06-14 18:54:14 暂无评论 300 JavaScript 闭包   循环   理解

提问者没有附上代码,根据描述,我猜测代码应当如下:

for (var i = 1; i < 6; i++)

{

setTimeout(function(){console.log(i);}, i * 1000);

}

这段循环将每隔1秒输出一次6,那么我们来解析一下这段代码:初始的i值为1,setTimeout将设置一个1*1000毫秒的timer,JS引擎中,timer的机制是将代码"function(){console.log(i);}压入队列,等待trigger去触发执行。但是循环代码的执行仍然是在初始的JS线程中,没有任何等待立即执行接下来的循环,于是压入了2*1000一直到5*1000的5个函数,这个过程几乎是不耗时的(循环5次对现在的CPU来说基本就是0毫秒)。所以从当前开始起计时,到1、2、3、4、5秒时均执行一次函数,在我们看来就是每隔1秒钟输出了一次i。

接下来看为什么输出了5次6而不是1-5,接着上面的过程,还在JS线程当中,循环到i=5压入timer函数后,i++仍然是要执行的,此时i的值是6,进入循环体判断i<6失败,循环结束。JS线程结束。

1000毫秒处的trigger触发后,JS解释器被传入代码"function(){console.log(i);}",i变量对于这个function块来说是undefined,解释器开始向上层块代码去搜索变量i,此例中只有两层,函数的上层就是JS环境的最顶层global,global中找到变量i,其值是6,于是控制台输出了6;后面4次trigger触发时原理相同,所以输出了5次6,间隔是1秒。

为了加深理解,我们把代码稍微修改一下:

for (var i = 1; i < 6; i++)

{

setTimeout((function(){console.log(i)})(), i * 1000);

}

结果是瞬间输出12345,因为(sourceCodeBlock)()这个行为是一个立即执行的行为,也就是在第一次的JS循环线程中,每一次循环就会执行一次sourceCodeBlock,它的上层块是循环体,循环体中的i变量就是当次循环中i的值。setTimeout只是将这个闭包block执行的结果压入了timer队列,这个执行结果到了trigger触发时在JS运行环境里既不是函数类型又不是表达式类型,没有任何意义,在引擎解释的阶段就会被优化掉。因此后面的5秒钟,浏览器没有任何动作。