for循環 嵌套延時器 實例及解決方案
首先 看一個經典的for循環嵌套延時器的案例
for(var i = 0;i<lg;i++){// lg = 6
setTimeout(function(){ console.log(i); //此時輸出為 6 個 6
},1000) }
我們想要的結果是在for循環中一次打印出 i 的 值。即0,1,2,3,4,5;但是輸出6個相同的個數字是什么原因呢?
這主傳進去要是因為setTimeout的執行時異步執行的,而for循環的執行卻非常的快,所以,在1s后執行定時器函數時, i 已經 循環到了最大值6,其他的i值已經被銷毀,此時再執行定時器,則是把 i=6傳進去了,所以造成了這樣的結果。
那我們應該怎么解決這個問題呢?
解決這個問題首先是要解決 i 值的變量銷毀問題,即瀏覽器的垃圾回收機制:
第一種方法:將延時器中的函數用一個自執行函數包起來,把每個循環中的 i 在被回收之前直接傳入到自執行函數中,這樣就可以避免被回收:如下:
for (var i = 0; i < lg; i++) {//lg = 6
setTimeout((function(a) { console.log(a);//操縱變量a,和i無關 此時輸出為0,1,2,3,4,5
})(i), 1000);//將 i 作為變量傳入
}
但是這樣寫會出現一個問題,函數直接打印了,並沒有一秒的延遲,原因是將自執行函數放在定時器中,會直接執行,並不是1秒后再執行,所以在這種方法上做了一些改進,即第二種方法:
第二種方法:將延時器整個的包裹在一個子執行函數中,這樣就相當於同時定義了6個延時1s的延時器:
for (var i = 0; i < lg; i++) {//lg = 6
(function(a){//自執行函數,獲取i
setTimeout(function() { console.log(a);//操縱變量a,和i無關 此時輸出為 0,1,2,3,4,5 且在1s延遲后輸出
}, 1000) })(i) }
這樣就完美的解決問題了;如果你想要每隔一秒輸出一個值,而不是同時輸出,則可以將參數傳進時間中:
for (var i = 0; i < lg; i++) {//lg = 6
(function(a){//自執行函數,獲取i
setTimeout(function() { console.log(a);//操縱變量a,和i無關 此時輸出為 0,1,2,3,4,5 且在1s延遲后輸出
}, a*1000)//將 i 的值傳進來 ,這樣就可以每個一秒輸出一個值
})(i) }
setInterval定時器和setTimeout 不同,因為是執行次數的原因,不能將 i 的值傳進時間中,會造成多次重復;