《你不知道的JS》读书笔记之闭包在循环中的应用

闭包在循环中的应用

延迟函数的回调会在循环结束时才执行;
事实上,当定时器运行时即便没给迭代中执行的是 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);
}
相关文章
相关标签/搜索