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

闭包与定时器
本文探讨了闭包在JavaScript循环中的应用,展示了如何通过不同方法解决定时器回调中变量捕获的问题,包括立即执行函数、参数传递、let声明等。

闭包在循环中的应用

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值