javascript - setTimeout第一個(gè)參數(shù)是立即執(zhí)行函數(shù),看不懂了
問(wèn)題描述
setTimeout第一個(gè)參數(shù)是立即執(zhí)行函數(shù),看不懂了
for (var i = 0; i < 5; i++) { setTimeout((function(i) { console.log(i); })(i), i * 1000);}
雖然結(jié)果是立即輸出0,1,2,3,4,但是不知道為啥
問(wèn)題解答
回答1:這樣其實(shí)是一下子全部打印出來(lái)的。。。不是每隔一秒打印出來(lái)。。建議這樣寫(xiě)。。
for (var i = 0; i < 5; i++) { setTimeout((function(i) { return function() {console.log(i); } })(i), i * 1000);}
我來(lái)解釋一下這個(gè)為什么可以獲取到0、1、2、3、4.網(wǎng)上關(guān)于JS預(yù)解釋的文章也不少,在進(jìn)入執(zhí)行上下文階段的時(shí)候函數(shù)并不會(huì)執(zhí)行,簡(jiǎn)單來(lái)說(shuō)就是當(dāng)你聲明這個(gè)函數(shù)的時(shí)候,只要不調(diào)用就不會(huì)執(zhí)行,上下文里面只會(huì)保存著這個(gè)函數(shù)的引用,可以看做這個(gè)函數(shù)保存在內(nèi)存中,只有到調(diào)用的時(shí)候函數(shù)才會(huì)執(zhí)行,我說(shuō)說(shuō)自己的理解,有不對(duì)的地方請(qǐng)指出來(lái)。如果沒(méi)有立即執(zhí)行函數(shù): 你在for循環(huán)里面實(shí)際上相當(dāng)于定義了5個(gè)定時(shí)器,但是js是單線(xiàn)程,這五個(gè)函數(shù)會(huì)被放到隊(duì)列里面等待執(zhí)行,舉個(gè)不一定恰當(dāng)?shù)睦樱憔桶堰@五個(gè)函數(shù)function() {console.log(i);}當(dāng)成字符串保存到內(nèi)存中,一直沒(méi)什么動(dòng)靜,等到這五個(gè)函數(shù)調(diào)用執(zhí)行的時(shí)候(就是setTimeout的第二個(gè)參數(shù)的時(shí)間到了的時(shí)候),才會(huì)開(kāi)始執(zhí)行這個(gè)函數(shù),因?yàn)楹瘮?shù)里面有個(gè)i,這個(gè)時(shí)候會(huì)通過(guò)作用域鏈來(lái)查找這個(gè)i,最后在外面的作用域里面查找到了i,但是這個(gè)時(shí)候for循環(huán)已經(jīng)執(zhí)行結(jié)束了,i已經(jīng)變成4了,所以會(huì)打印出5個(gè)4.如果有立即執(zhí)行函數(shù)(比如我上面寫(xiě)的那個(gè)): 你在for循環(huán)里面實(shí)際上相當(dāng)于定義了5個(gè)定時(shí)器,但是js是單線(xiàn)程,這五個(gè)函數(shù)會(huì)被放到隊(duì)列里面等待執(zhí)行。
(function(i) {
return function() { console.log(i);}
})(i)
但是由于外面是立即執(zhí)行函數(shù),所以會(huì)立即就執(zhí)行了,并且把i傳了進(jìn)去,等到這五個(gè)函數(shù)執(zhí)行的時(shí)候,向上查找i,正好在這個(gè)立即調(diào)用函數(shù)的作用域里面查找到了i,所以會(huì)打印出0、1、2、3、4.
回答2:你先看括住整個(gè)function的那個(gè)括號(hào)也就是:
(function (i) { console.log(i);})
這一段,理解就是把自己包成一個(gè)包裹,是一個(gè)整體,這個(gè)整體是一個(gè)函數(shù)
那么接下來(lái)怎么調(diào)用函數(shù)呢?是不是函數(shù)名()這樣,在函數(shù)名后面加上括號(hào),并且可以傳遞參數(shù)進(jìn)去
所以很自然的,就出現(xiàn)了這樣:
(function (i) { console.log(i);})(i)
這種調(diào)用,就是寫(xiě)個(gè)函數(shù),用()包起來(lái),在后面接個(gè)(),里面寫(xiě)參數(shù),就直接執(zhí)行了
回答3:就像樓上說(shuō)的是立刻打印出來(lái)了,你的setTimeout根本就沒(méi)起作用。原因就是立即執(zhí)行函數(shù)執(zhí)行后沒(méi)有返回值,所以相當(dāng)于setTimeout(undefined, i*1000)。
回答4:setTimeout要求第1個(gè)參數(shù)是一個(gè)函數(shù),這樣等第2個(gè)參數(shù)規(guī)定的時(shí)間到了之后,開(kāi)始執(zhí)行第1個(gè)參數(shù)定義的函數(shù)。
當(dāng)你這么寫(xiě)的時(shí)候:
for (var i = 0; i < 5; i++) { setTimeout(function(i) { console.log(i); }, i * 1000);}
你會(huì)注意到定時(shí)器已經(jīng)起作用了,只不過(guò)是每隔1秒種打出來(lái)一個(gè)undefined。因?yàn)楫?dāng)執(zhí)行這個(gè)函數(shù)的時(shí)候,i已經(jīng)從循環(huán)中跳出,已經(jīng)沒(méi)有值了。
所以你改成這樣:
for (var i = 0; i < 5; i++) { setTimeout((function(i) { console.log(i); })(i), i * 1000);}
但在這種情況下,第1個(gè)參數(shù)不是一個(gè)函數(shù),而是一個(gè)表達(dá)式,也就是說(shuō)會(huì)立即執(zhí)行的函數(shù),它不會(huì)等到計(jì)時(shí)器起作用才執(zhí)行,而是只要一碰到就會(huì)執(zhí)行,所以表現(xiàn)形式就是直接打出了0,1,2,3,4。
按照樓上的說(shuō)法改成這樣:
for (var i = 0; i < 5; i++) { setTimeout((function(i) { return function() {console.log(i); } })(i), i * 1000);}
雖然第1個(gè)參數(shù)是一個(gè)表達(dá)式,還是會(huì)立即執(zhí)行,但是這個(gè)表達(dá)式執(zhí)行的結(jié)果不是輸出數(shù)值,而是返回一個(gè)函數(shù),這就滿(mǎn)足了setTimeout對(duì)第1個(gè)參數(shù)是函數(shù)的要求,并且給定了正確的輸入?yún)?shù),所以每隔1秒種會(huì)輸出一個(gè)正確的結(jié)果。
不過(guò),為了團(tuán)隊(duì)協(xié)作起見(jiàn),我一般不建議這么寫(xiě),我建議還是規(guī)規(guī)矩矩按照setTimeout的標(biāo)準(zhǔn)寫(xiě)法寫(xiě)成這樣:
var j = 0;for (i = 0; i < 5; i++) { setTimeout(function() { console.log(j); j++; }, i * 1000);}
這樣至少對(duì)于組內(nèi)其他成員讀起來(lái)更容易理解一些。
回答5:因?yàn)槭橇⒓磮?zhí)行的函數(shù)啊,當(dāng)然立即輸出了
回答6:函數(shù)作用域問(wèn)題,改變this指向就可以了。
for (var i = 0; i < 5; i++) { setTimeout((function(i) {console.log(i); }).bind(this,i), i * 1000);}回答7:
沒(méi)有立即執(zhí)行函數(shù)的話(huà),打印出來(lái)的是5個(gè)5,而且是每隔一秒打印,因?yàn)檫@里setTimeout里面的函數(shù)要等循環(huán)完成之后才會(huì)執(zhí)行,這時(shí)全局變量i就是5了。使用立即執(zhí)行函數(shù),會(huì)獲取循環(huán)中的每一個(gè)i,這里有閉包的效果,這個(gè)i這時(shí)就是一個(gè)局部變量了,存在于此函數(shù)中,每次執(zhí)行時(shí)變量i的值都不一樣。
回答8:(function(i) { console.log(i);})(i)
聲明了馬上執(zhí)行 這樣 就是 0 1 2 3 4
然而這個(gè)沒(méi)有返回值 因此默認(rèn)是 undefined
因此你的代碼可以認(rèn)為是這樣:
for (var i = 0; i < 5; i++) { var temp = (function(i) { console.log(i); })(i); // temp 是 undefined setTimeout(temp, i * 1000);}

理解一下原理,閉包,堆棧,事件隊(duì)列,同步,異步
翻轉(zhuǎn)一下思路:把傳進(jìn)去的參數(shù)i去掉,看看是不是能正常打印,不能的話(huà),分析一下為什么
能不能換幾種寫(xiě)法,實(shí)現(xiàn)上面一樣的效果,分析一下為什么完成上面的幾點(diǎn),你就知道原因 過(guò)程 結(jié)果

網(wǎng)公網(wǎng)安備