面試中在談論閉包的時候有讓實現一個倒計時功能,當時滿腦子都是那個setTimeout壓入棧不斷輸出循環尾數的例子,最後也是沒有用閉包實現倒計時
首先來看看那個在循環中不斷重複輸出的setTimeout代碼片:
var countDown = function(){
for(var i=10;i>0;i--)
{
setTimeout(function(){console.log(i);},1000);
}
}
輸出效果:
當然這個是沒有實現倒計時功能的,原因呢:
首先,和setTimeout設置的時間是沒有關係的,很多人懷疑是時間設置的問題,不信可以將時間設置爲0,結果是一樣的,這裏就不演示了
當然,得分析分析setTimeout的循環機制:setTimeout是從隊列結束的時候開始計時的,如果前面有進程沒有結束,那麼它就等到它結束再開始計時,在這裏呢,任務隊列就是它自己所在的循環。循環結束setTimeout纔開始計時,所以無論如何,setTimeout裏面的i都是最後一次循環的
解決方法呢如下:
var countDown = function(){
for(var i=10;i>0;i--)
{
var a =function(v){
return function(){
console.log(v);
}
}
setTimeout(a(i),1000);
}
}
因爲setTimeout的第一個參數需要一個函數,所以返回一個函數給他,返回的同時把i作爲參數傳進去,通過形參v緩存了i,並帶進返回的函數裏面
當然了,這樣寫也是可以的:
var countDown = function(){
for(var i=10;i>0;i--)
{
var a =function(v){
console.log(v);
}
setTimeout(a(i),1000);
}
}
總之呢:例子中遇到setTimeout的問題,原因就是回調等待循環隊列結束造成的,解決的辦法就是給回調函數傳一個實參緩存循環的數據