闭包在循环中的应用
延迟函数的回调会在循环结束时才执行;
事实上,当定时器运行时即便没给迭代中执行的是
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);
}