84669 人學習
152542 人學習
20005 人學習
5487 人學習
7821 人學習
359900 人學習
3350 人學習
180660 人學習
48569 人學習
18603 人學習
40936 人學習
1549 人學習
1183 人學習
32909 人學習
setTimeout第一個參數是立即執行函數,看不懂了
for (var i = 0; i < 5; i++) { setTimeout((function(i) { console.log(i); })(i), i * 1000); }
雖然結果是立即輸出0,1,2,3,4,但是不知道為啥
光阴似箭催人老,日月如移越少年。
這樣其實是一下子全部印出來的。 。 。不是每隔一秒列印出來。 。建議這樣寫。 。
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)
(function(i) {
return function() { console.log(i); }
})(i)
但是由於外面是立即執行函數,所以會立即就執行了,並且把i傳了進去,等到這五個函數執行的時候,向上查找i,正好在這個立即調用函數的作用域裡面查找到了i,所以會印出0、1、2、3、4.
你先看括住整個function的那個括號也就是:
(function (i) { console.log(i); })
這一段,理解就是把自己包成一個包裹,是一個整體,這個整體是一個函數
那接下來要怎麼呼叫函數呢?是不是函數名稱()這樣,在函數名稱後面加上括號,可以傳遞參數進去
所以很自然的,就出現了這樣:
(function (i) { console.log(i); })(i)
這種調用,就是寫個函數,用()包起來,在後面接個(),裡面寫參數,就直接執行了
就像樓上說的是立刻印出來了,你的setTimeout根本就沒起作用。 原因就是立即執行函數執行後沒有回傳值,所以相當於setTimeout(undefined, i*1000)。
setTimeout要求第1個參數是一個函數,這樣等第2個參數規定的時間到了之後,開始執行第1個參數定義的函數。
setTimeout
當你這麼寫的時候:
for (var i = 0; i < 5; i++) { setTimeout(function(i) { console.log(i); }, i * 1000); }
你會注意到定時器已經起作用了,只不過是每隔1秒種打出來一個undefined。因為執行這個函數的時候,i已經從迴圈中跳出,已經沒有值了。
undefined
所以你改成這樣:
但在這種情況下,第1個參數不是一個函數,而是一個表達式,也就是說會立即執行的函數,它不會等到計時器起作用才執行,而是只要一碰到就會執行,所以表現形式就是直接打出了0,1,2,3,4。
依照樓上的說法改成這樣:
雖然第1個參數是一個表達式,還是會立即執行,但是這個表達式執行的結果不是輸出數值,而是返回一個函數,這就滿足了setTimeout對第1個參數是函數的要求,並且給定了正確的輸入參數,所以每隔1秒種會輸出一個正確的結果。
不過,為了團隊協作起見,我一般不建議這麼寫,我建議還是規規矩矩按照setTimeout的標準寫法寫成這樣:
var j = 0; for (i = 0; i < 5; i++) { setTimeout(function() { console.log(j); j++; }, i * 1000); }
這樣至少對於組內其他成員讀起來更容易理解一些。
因為是立即執行的函數啊,當然立即輸出了
函數作用域問題,改變this指向就可以了。
for (var i = 0; i < 5; i++) { setTimeout((function(i) { console.log(i); }).bind(this,i), i * 1000); }
沒有立即執行函數的話,印出來的是5個5,而且是每隔一秒列印,因為這裡setTimeout裡面的函數要等循環完成之後才會執行,這時全域變數i就是5了。使用立即執行函數,會取得迴圈中的每一個i,這裡有閉包的效果,這個i這時就是一個局部變數了,存在於此函數中,每次執行時變數i的值都不一樣。
(function(i) { console.log(i); })(i)
聲明了馬上執行 這樣 就是 0 1 2 3 4
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去掉,看看是不是能正常打印,不能的話,分析一下為什麼
能不能換幾種寫法,實現上面一樣的效果,分析一下為什麼完成上面的幾點,你就知道原因 過程 結果
這樣其實是一下子全部印出來的。 。 。不是每隔一秒列印出來。 。建議這樣寫。 。
我來解釋一下這個為什麼可以獲取到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是單線程,這五個函數會放到佇列裡面等待執行。
但是由於外面是立即執行函數,所以會立即就執行了,並且把i傳了進去,等到這五個函數執行的時候,向上查找i,正好在這個立即調用函數的作用域裡面查找到了i,所以會印出0、1、2、3、4.
你先看括住整個function的那個括號
也就是:
這一段,理解就是把自己包成一個包裹,是一個整體,這個整體是一個函數
那接下來要怎麼呼叫函數呢?是不是函數名稱()這樣,在函數名稱後面加上括號,可以傳遞參數進去
所以很自然的,就出現了這樣:
這種調用,就是寫個函數,用()包起來,在後面接個(),裡面寫參數,就直接執行了
就像樓上說的是立刻印出來了,你的setTimeout根本就沒起作用。
原因就是立即執行函數執行後沒有回傳值,所以相當於setTimeout(undefined, i*1000)。
setTimeout
要求第1個參數是一個函數,這樣等第2個參數規定的時間到了之後,開始執行第1個參數定義的函數。當你這麼寫的時候:
你會注意到定時器已經起作用了,只不過是每隔1秒種打出來一個
undefined
。因為執行這個函數的時候,i已經從迴圈中跳出,已經沒有值了。所以你改成這樣:
但在這種情況下,第1個參數不是一個函數,而是一個表達式,也就是說會立即執行的函數,它不會等到計時器起作用才執行,而是只要一碰到就會執行,所以表現形式就是直接打出了0,1,2,3,4。
依照樓上的說法改成這樣:
雖然第1個參數是一個表達式,還是會立即執行,但是這個表達式執行的結果不是輸出數值,而是返回一個函數,這就滿足了setTimeout對第1個參數是函數的要求,並且給定了正確的輸入參數,所以每隔1秒種會輸出一個正確的結果。
不過,為了團隊協作起見,我一般不建議這麼寫,我建議還是規規矩矩按照setTimeout的標準寫法寫成這樣:
這樣至少對於組內其他成員讀起來更容易理解一些。
因為是立即執行的函數啊,當然立即輸出了
函數作用域問題,改變this指向就可以了。
沒有立即執行函數的話,印出來的是5個5,而且是每隔一秒列印,因為這裡setTimeout裡面的函數要等循環完成之後才會執行,這時全域變數i就是5了。使用立即執行函數,會取得迴圈中的每一個i,這裡有閉包的效果,這個i這時就是一個局部變數了,存在於此函數中,每次執行時變數i的值都不一樣。
聲明了馬上執行 這樣 就是
0 1 2 3 4
然而這個沒有回傳值 因此預設是
undefined
因此你的程式碼可以認為是這樣:
理解一下原理,閉包,堆疊,事件隊列,同步,非同步
翻轉一下思路:把傳進去的參數i去掉,看看是不是能正常打印,不能的話,分析一下為什麼
能不能換幾種寫法,實現上面一樣的效果,分析一下為什麼
完成上面的幾點,你就知道原因 過程 結果