之前去面試一家公司時,面試官出了一道關於js的setTimeout函數的題目:
1 /*
2 *面試官給的原題目如下: 3 *執行mytest()后,控制台輸出內容是_____ 4 *function mytest() { 5 * for(var i = 0; i < 5; i ++) 6 * setTimeout(console.log(i),0); 7 *} 8 *但這應該不是面試官的問題,當時我也沒有發現問題,回來測試后才發現,這個函數不是 9 *面試官要表達的意思。 10 */
11 //正確的代碼如下:
12 function mytest() { 13 for(var i = 0; i < 5; i ++) 14 setTimeout(function(){ 15 console.log(i); 16 },0); 17 }
我的回答是:控制台輸出為5 5 5 5 5,雖然答案對了,但是解釋就太牽強了,我說是因為for語句之執行速度比setTimeout函數快,面試笑了笑,嗚嗚~~(這有毛關系)。
后來網上找了一些資料,參考了一些書籍,這里我給出一個靠譜的解釋:
首先,我們必須承認,js是單線程的,即使是對於ajax異步方式或者像setTimeout這樣的函數。
其次,我們要理解js函數的執行過程,對於setTimeout這樣的函數來說並不是每次都能按照預定延遲的時間執行指定函數的。下面舉一個列子:
比如有一個函數fun0在執行開始時創建了一個定時器T1,T1定時器將在200ms后被觸發指定函數。這時我們需要考慮一個問題,假設fun0的執行時間為250ms(大於200ms),那將會怎樣?前面已經說過,js是單線程的,所以不存在fun0和定時器同時執行的情況。這時候定時器制定的函數會在fun0執行完后才執行,定時器的等待時間為250ms,並不是我們指定的200ms。
對於javascript這樣的執行方式,我們可以想象在函數執行過程中有兩個隊列。隊列Q1是指執行隊列,每次只能執行一個函數;隊列Q2就是等待隊列,存放着將要執行的函數。每當有一個函數要執行時,就會先把這個函數放進等待隊列,如果Q1為空,那么久立即執行這個函數。當然在大多數情況下,函數都是立即執行的。
因此,我們可以知道,setTimeout定時器指定的函數必須要在當前執行隊列為空時才會執行。
現在我們再來分析一下上面那道題,很顯然,每次的for循環都觸發了一個定時函數,這些定時函數有點特殊,是立即執行的(如果執行隊列為空的話)。但是在每次觸發時,for循環都還未結束,也就是執行隊列不為空,此時新建的定是函數只能放在等待隊列里無法立即執行。當最后一次for循環執行結束后,執行隊列變為空,這時等待隊列的函數就立即進入到了執行隊列,於是就開始執行只控制台輸出。因為setTimeout指定的匿名函數中i的值是一種引用值(自行谷歌腦補),所以輸出結果為5 5 5 5 5。
如描述或理解有誤,歡迎各位留言!!