javascript - setTimeout第一個參數是立即執行函數,看不懂了
問題描述
setTimeout第一個參數是立即執行函數,看不懂了
for (var i = 0; i < 5; i++) { setTimeout((function(i) { console.log(i); })(i), i * 1000);}
雖然結果是立即輸出0,1,2,3,4,但是不知道為啥
問題解答
回答1:這樣其實是一下子全部打印出來的。。。不是每隔一秒打印出來。。建議這樣寫。。
for (var i = 0; i < 5; i++) { setTimeout((function(i) { return function() {console.log(i); } })(i), i * 1000);}
我來解釋一下這個為什么可以獲取到0、1、2、3、4.網上關于JS預解釋的文章也不少,在進入執行上下文階段的時候函數并不會執行,簡單來說就是當你聲明這個函數的時候,只要不調用就不會執行,上下文里面只會保存著這個函數的引用,可以看做這個函數保存在內存中,只有到調用的時候函數才會執行,我說說自己的理解,有不對的地方請指出來。如果沒有立即執行函數: 你在for循環里面實際上相當于定義了5個定時器,但是js是單線程,這五個函數會被放到隊列里面等待執行,舉個不一定恰當的例子,你就把這五個函數function() {console.log(i);}當成字符串保存到內存中,一直沒什么動靜,等到這五個函數調用執行的時候(就是setTimeout的第二個參數的時間到了的時候),才會開始執行這個函數,因為函數里面有個i,這個時候會通過作用域鏈來查找這個i,最后在外面的作用域里面查找到了i,但是這個時候for循環已經執行結束了,i已經變成4了,所以會打印出5個4.如果有立即執行函數(比如我上面寫的那個): 你在for循環里面實際上相當于定義了5個定時器,但是js是單線程,這五個函數會被放到隊列里面等待執行。
(function(i) {
return function() { console.log(i);}
})(i)
但是由于外面是立即執行函數,所以會立即就執行了,并且把i傳了進去,等到這五個函數執行的時候,向上查找i,正好在這個立即調用函數的作用域里面查找到了i,所以會打印出0、1、2、3、4.
回答2:你先看括住整個function的那個括號也就是:
(function (i) { console.log(i);})
這一段,理解就是把自己包成一個包裹,是一個整體,這個整體是一個函數
那么接下來怎么調用函數呢?是不是函數名()這樣,在函數名后面加上括號,并且可以傳遞參數進去
所以很自然的,就出現了這樣:
(function (i) { console.log(i);})(i)
這種調用,就是寫個函數,用()包起來,在后面接個(),里面寫參數,就直接執行了
回答3:就像樓上說的是立刻打印出來了,你的setTimeout根本就沒起作用。原因就是立即執行函數執行后沒有返回值,所以相當于setTimeout(undefined, i*1000)。
回答4:setTimeout要求第1個參數是一個函數,這樣等第2個參數規定的時間到了之后,開始執行第1個參數定義的函數。
當你這么寫的時候:
for (var i = 0; i < 5; i++) { setTimeout(function(i) { console.log(i); }, i * 1000);}
你會注意到定時器已經起作用了,只不過是每隔1秒種打出來一個undefined。因為當執行這個函數的時候,i已經從循環中跳出,已經沒有值了。
所以你改成這樣:
for (var i = 0; i < 5; i++) { setTimeout((function(i) { console.log(i); })(i), i * 1000);}
但在這種情況下,第1個參數不是一個函數,而是一個表達式,也就是說會立即執行的函數,它不會等到計時器起作用才執行,而是只要一碰到就會執行,所以表現形式就是直接打出了0,1,2,3,4。
按照樓上的說法改成這樣:
for (var i = 0; i < 5; i++) { setTimeout((function(i) { return function() {console.log(i); } })(i), i * 1000);}
雖然第1個參數是一個表達式,還是會立即執行,但是這個表達式執行的結果不是輸出數值,而是返回一個函數,這就滿足了setTimeout對第1個參數是函數的要求,并且給定了正確的輸入參數,所以每隔1秒種會輸出一個正確的結果。
不過,為了團隊協作起見,我一般不建議這么寫,我建議還是規規矩矩按照setTimeout的標準寫法寫成這樣:
var j = 0;for (i = 0; i < 5; i++) { setTimeout(function() { console.log(j); j++; }, i * 1000);}
這樣至少對于組內其他成員讀起來更容易理解一些。
回答5:因為是立即執行的函數啊,當然立即輸出了
回答6:函數作用域問題,改變this指向就可以了。
for (var i = 0; i < 5; i++) { setTimeout((function(i) {console.log(i); }).bind(this,i), i * 1000);}回答7:
沒有立即執行函數的話,打印出來的是5個5,而且是每隔一秒打印,因為這里setTimeout里面的函數要等循環完成之后才會執行,這時全局變量i就是5了。使用立即執行函數,會獲取循環中的每一個i,這里有閉包的效果,這個i這時就是一個局部變量了,存在于此函數中,每次執行時變量i的值都不一樣。
回答8:(function(i) { console.log(i);})(i)
聲明了馬上執行 這樣 就是 0 1 2 3 4
然而這個沒有返回值 因此默認是 undefined
因此你的代碼可以認為是這樣:
for (var i = 0; i < 5; i++) { var temp = (function(i) { console.log(i); })(i); // temp 是 undefined setTimeout(temp, i * 1000);}
理解一下原理,閉包,堆棧,事件隊列,同步,異步
翻轉一下思路:把傳進去的參數i去掉,看看是不是能正常打印,不能的話,分析一下為什么
能不能換幾種寫法,實現上面一樣的效果,分析一下為什么完成上面的幾點,你就知道原因 過程 結果
