闭包在循环中的应用

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

利用延迟函数向其回调函数中传参,为每个迭代中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);
}

吉叶
46 声望4 粉丝