閉包在循環中的應用
延遲函數的回調會在循環結束時才執行;
事實上,當定時器運行時即便沒給迭代中執行的是
setTime(..., 0)
,多有的回調函數依然是在循環結束後纔會被執行,所以會每次輸出一個6出來。
for(var i=1; i<=5; i++){
setTimeout( function timer(){
let temp = new Date();
console.log(i + "----" + temp.toLocaleTimeString() + "." + temp.getMilliseconds());
}, i*1000);
if(i == 5){
var now = new Date();
console.log("for循環結束----"+now.toLocaleTimeString() + "." + now.getMilliseconds());
}
}
// for循環結束----下午7:51:29.885
// 6----下午7:51:30.885
// 6----下午7:51:31.885
// 6----下午7:51:32.885
// 6----下午7:51:33.885
// 6----下午7:51:34.885
利用當即執行函數建立做用域,但做用域爲空(沒有傳參),並未奏效;
for (var i = 0; i <= 5; i++){
(function(){
setTimeout(function timer(){
let temp = new Date();
console.log(i + "----" + temp.toLocaleTimeString() + "." + temp.getMilliseconds());
}, i*1000)
})();
if(i == 5){
var now = new Date();
console.log("for循環結束----"+now.toLocaleTimeString() + "." + now.getMilliseconds());
}
}
在當即執行函數中捕獲一個變量
for (var i = 0; i <= 5; i++){
(function(){
var j = i; // IIFE有了本身的變量
setTimeout(function timer(){
let temp = new Date();
console.log(j + "----" + temp.toLocaleTimeString() + "." + temp.getMilliseconds());
}, j*1000)
})();
if(i == 5){
var now = new Date();
console.log("for循環結束----"+now.toLocaleTimeString() + "." + now.getMilliseconds());
}
}
// for循環結束----下午8:14:28.915
// 0----下午8:14:28.916
// 1----下午8:14:29.916
// 2----下午8:14:30.916
// 3----下午8:14:31.916
// 4----下午8:14:32.916
// 5----下午8:14:33.916
改進
1. 利用當即執行函數(IIFE)傳參
利用當即執行函數爲每一個迭代都生成一個新的做用域,使得延遲函數的回調能夠將新的做用域封閉在每一個迭代內部;
for (var i = 0; i <= 5; i++){
(function(j){
setTimeout(function timer(){
let temp = new Date();
console.log(j + "----" + temp.toLocaleTimeString() + "." + temp.getMilliseconds());
}, j*1000)
})(i);
}
2. 利用setTimeout
給回調函數(callback
)中傳參,產生timer
對for
循環做用域的閉包
利用延遲函數向其回調函數中傳參,爲每一個迭代中
callback
中生成新的做用域,使得延遲函數的回調能夠將新的做用域封閉在每一個迭代內部;
for (var i = 0; i <= 5; i++){
setTimeout(function timer(j){
let temp = new Date();
console.log(j + "----" + temp.toLocaleTimeString() + "." + temp.getMilliseconds());
}, i*1000, i);
}
3. 利用let
聲明劫持塊做用域
本質:將一個塊轉換成了一個能夠被關閉的做用域。
for(var i=0; i<5; i++){
let j = i;// 閉包的塊做用域
setTimeout(function timer(){
console.log(j);
}, j*1000);
}
4. 利用for
循環頭部的let
聲明
for
循環頭部的
let
聲明的特殊行爲:使得變量
i
在循環過程當中不止被聲明一次,
每次迭代都會聲明。
隨後的每一個迭代都會使用上一個迭代結束時的值來初始化這個變量。
for(let i=0; i<5; i++){
setTimeout(function timer(){
console.log(i);
}, i*1000);
}