1. 程式人生 > 實用技巧 >關於setTimeout()與for(){}迴圈之間的糾纏不清

關於setTimeout()與for(){}迴圈之間的糾纏不清

問題寫法

for(var i = 1; i <= 5; i++) {
    setTimeout( function(){
        console.log(i);
    },i*1000);
}
console.log(i);
// 先輸出環境執行產生的一個6,每隔1s,再輸出一個6,連續輸出5個6
  • 解析:
    • 此處的i是由var定義在全域性作用域內,輸出值i屬於全域性作用域的值
    • setTimeout()函式執行機制是,等全域性環境執行完成後,再執行回撥函式
    • 因此,i的終值是都是全域性變數i的值
    • 因為回撥函式並不是隨著環境一起執行的,而是等到環境函式執行完成後再集中執行的
    • 因此,五次迴圈相當於一起執行五次setTimeout()函式
    • 所以累加的秒數就相當於,每隔一秒執行一次

let定義寫法

for(let i = 1; i <= 5; i++) {
    setTimeout( function(){
        console.log(i);
    },i*1000);
}
console.log(i);
// 先輸出i未定義,再每隔1s輸出累加值
  • 解析:
    • 此處的i是由let定義在塊級作用域內,輸出值i屬於塊級作用域的值
    • 因此當i值出現變化時,是屬於內部作用域的變化
    • 而setTimeout()函式執行機制是,等環境執行完成後,再執行回撥函式
    • 因此,當i值出現變化時,已經沒有環境程式在運行了
    • 所以,此時for迴圈是跟著setTimeout()函式執行的
    • 因此,i的輸出值是for迴圈的累加值

立即函式寫法

for(var i = 1; i <= 5; i++) {
    (function(i){
        setTimeout( function(){
            console.log(i);
        },i*1000);
    })(i);
}
console.log(i);
// 先輸出i未定義,再每隔1s輸出累加值
  • 解析:
    • 此處的i是由var定義在全域性作用域內,輸出值i屬於全域性作用域的值
    • 雖然,setTimeout()函式執行機制是,等全域性環境執行完成後,再執行回撥函式
    • 但是,每次的for迴圈執行時,內部的立即函式都會執行
    • 而且,i的值會由形參i傳入到setTimeout()函式內部
    • 從而形成關於i的私有作用域
    • 因此此時的setTimeout()函式是和環境程式一起行動的
    • 而最外圍的i的輸出沒有時延,因此會先輸出

閉包函式寫法

for (var i = 1; i <= 5; i++) {
	setTimeout(function(i) {
		return function(){
			console.log(i);
		}
	}(i), i*1000);
}
console.log(i);
// 先輸出i未定義,再每隔1s輸出累加值
  • 解析:
    • 此處的i是由var定義在全域性作用域內,輸出值i屬於全域性作用域的值
    • setTimeout()函式執行機制是,等全域性環境執行完成後,再執行回撥函式
    • 而此處的閉包寫法,已然將for迴圈中i的變數值通過形參i保存於return機制中
    • 形成私有作用域
    • 待setTimeout()進行回撥函式執行時,i將輸出私域內的累加值

錯誤寫法

for(var i = 1; i <= 5; i++) {
    setTimeout(function(i){
        console.log(i);
    }(i),i*1000);
}
console.log(i);
  • 解析:
    • 此處的setTimeout()函式內的回撥函式
    • 在主程式執行時,已經隨著主程式一起執行完畢
    • 待setTimeout()非同步執行時,已無回撥函式可用