for 循环中 i 的按顺序输出


 	for(var i = 0; i < 5; i++){
        setTimeout(function() {
            console.log(i)
        },50)
    }
    console.log('哦吼~')
    // '哦吼~'
    //5
    //5
    //5
    //5
    //5
        

为什么打印结果是先打印哦吼,然后再是5个5呢?

首先,js是单线程的,也就是说一次只执行一个事件. 但是又分为主线程和事件队列, 对于回调函数这种异步的操作,会被加入到事件队列,等待主线程空闲的时候来执行事件队列里的事件

计时器并不是每隔一段时间执行处理函数,而是每隔一定时间将处理函数放入事件队列,等待主线程空闲的时候来执行

那么上述代码可以解析为:

		for (var i = 0; i < 5; i++) {
            
        }
        console.log('哦吼~'); // 主线程结束
        // 进入事件队列执行, 此时的 i 已经是5了
        setTimeout(function () {
            console.log(i)
        })
        setTimeout(function () {
            console.log(i)
        })
        setTimeout(function () {
            console.log(i)
        })
        setTimeout(function () {
            console.log(i)
        })
        setTimeout(function () {
            console.log(i)
        })

那么我们怎么让 for 循环中的 i 可以按顺序输出呢?

方法一: 将 for 循环放入计时器内部

        setTimeout(()=>{
            for(var i = 0; i < 5; i++) {
                console.log(i)
            }
        },50)
        console.log('哦吼~')
        // '哦吼~'
   	    //0
        //1
        //2
        //3
        //4

这个就不解释了吧

方法二: 将计时器放在自执行函数里

		for (var i = 0; i < 5; i++) { // var 存在声明提升,所以每次 循环执行的是赋值语句, 不会重复声明,
		实际上 i 只被声明了一次
            ((i) => {
                setTimeout(() => console.log(i))
            })(i)
        }
        console.log('哦吼~')
        // '哦吼~'
   	    //0
        //1
        //2
        //3
        //4

( 用箭头函数写的,看着很恶心,希望以后我自看的时候看得懂 )
自执行函数会形成一个作用域,而计时器形成了一个闭包,可以访问自执行函数保存下来的变量 i,即使自执行函数被调用了,他的作用域还在,闭包中仍然可以访问到 i,就相当于用自执行函数保存下来了不同的i

方法三: ES6中的 let 关键字

        for(let i = 0; i < 5; i++) {
            setTimeout(function () {
                console.log(i)
            },50)
        }
        console.log('哦吼~')
        // '哦吼~'
   	    //0
        //1
        //2
        //3
        //4

1. 由于 var 命令的变量提升机制, var 命令实际上只会执行一次
2. 而 let 命令不存在变量提升, 所以每次循环都会执行一次, 声明一个新变量( 但初始化的值不一样 ).
3. for 的每次循环都是不同的块级作用域, let 声明的变量是块级作用域的, 所以不存在重复声明的问题.
4. let 声明变量的 for 循环里, 每个匿名函数实际上引用的都是一个新的变量.

		{
            let i = 0
            if(i<5){
                setTimeout(() => console.log(i), 50)
                num = i
            }
        }
        {   num++
            let i = num
            if(i<5){
                setTimeout(() => console.log(i), 50)
            } 
        }
        {   num++
            let i = num
            if(i<5){
                setTimeout(() => console.log(i), 50)
            } 
        }
        {   num++
            let i = num
            if(i<5){
                setTimeout(() => console.log(i), 50)
            } 
        }
        {   num++
            let i = num
            if(i<5){
                setTimeout(() => console.log(i), 50)
            } 
        }

上面的代码仅仅是为了方便理解,let 关键字形成的块级作用域产生了类似闭包的效果, 并且在 for 循环中使用 let 来定义循环遍历还会有一个特殊效果: 每一次循环都会重新声明变量 i, 随后的每个循环都会使用上一个循环结束时的值(完成了 +1 之后的值)来初始化这个变量

方法四: try catch 语句

        for (var i = 0; i < 5; i++) {
            try {
                throw (i)
            } catch (j) {
                setTimeout(function () {
                    console.log(j);
                });
            }
        }
        console.log('哦吼~')
        // '哦吼~'
   	    //0
        //1
        //2
        //3
        //4

catch块中会指定一个标识符( 上面例子中的 j ), 这个标识符用来保存 throw 出来的值( 也就是 i ). catch 块会形成一个会计作用域, 所以每次循环抛出来的 i 都被保存到了不同的作用域里面, 类似于闭包

总结参考了很多前辈的文章,希望对你有用~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值