For循環(huán)+setTimeout中那些趣事

今天面試再次遇到for+setTimeout問題。再次仔細(xì)品嘗了一下,也算獲取更多一點(diǎn)知識(shí),接下來,我們把它完全搞清楚。

第一個(gè)例子

for(var i=0;i<10;i++){? setTimeout(console.log(i),1000);//0-10}
這個(gè)可能很多人一上來會(huì)說應(yīng)該打印10個(gè)10,其實(shí)不是,是0-9.為什么呢,因?yàn)閏onsole.log()是立即執(zhí)行函數(shù)也就是IIFE,而console.log只是一個(gè)function 函數(shù)名。所以console.log()是同步任務(wù)跟for循環(huán)是同步執(zhí)行的,而setTimeout()是異步任務(wù)需要等到主線程的同步任務(wù)執(zhí)行完畢后才能執(zhí)行,所以結(jié)果就是0、1、2、3、4、5、6、7、8、9了。
第二個(gè)例子

for(var i=0;i<10;i++){? setTimeout(()=>{? ? ?console.log(i)? },1000);//10個(gè)10}
這個(gè)應(yīng)該都熟悉,打印10個(gè)10,因?yàn)閟etTimeout是異步函數(shù),加上打印的是外面的變量,因?yàn)槭褂胿ar 聲明變量i。相當(dāng)于下面這樣,等隊(duì)列任務(wù)處理完,輪到打印的時(shí)候,i已經(jīng)是10啦。
var i;for(i=0;i<10;i++){? setTimeout(()=>{? ? ?console.log(i)? },1000);//10個(gè)10}
第三個(gè)例子

for (var i=1; i<=5; i++) {? ?(function(j) {? ? ? ?setTimeout( function timer() {? ? ? ? ? ?console.log( j );? ? ? ?}, j*1000 );? ?})(i);}
這個(gè)稍微不一樣,原因是有了閉包,很多同志還不清楚閉包到底是什么。一句話就是:在一個(gè)嵌套函數(shù)中,函數(shù)內(nèi)部可以引用外部的參數(shù)和變量,并且不被GC回收,也就是函數(shù)內(nèi)部被其它引用了。
第四個(gè)例子

for (let i=1; i<=5; i++) {? ?setTimeout( function timer() {? ? ? ?console.log( i );? ? }, i*1000 );}
這個(gè)沒有什么說的用了let,每次循環(huán)會(huì)有單獨(dú)的作用域。
春生萬物生

function timer(i) {? ?setTimeout( console.log( i ), i*1000 );}for (var i=1; i<=5;i++) {? ?timer(i);}
這個(gè)又是為什么呢,仔細(xì)一看,就和第一個(gè)一樣的,只是視野混淆了。也是IIFE。

總結(jié)
1、IIFE、閉包一類都是解決它最后打印的是它循環(huán)的最后一個(gè)值。

