操作符i++、++i与setTimeout

没啥区别的使用

在 for 循环中,++i 和 i++ 都是用于递增变量 i 的操作符,但它们的使用方式略有不同。

  1. ++i:这是前缀递增操作符。
    它会先将变量 i 的值加 1,然后返回递增后的值。在 for 循环中,使用 ++i 可以在每次迭代之前递增 i 的值。

    示例:

    for (let i = 0; i < 5; ++i) {
      console.log(i);
    }
    // 输出:0 1 2 3 4
    
  2. i++:这是后缀递增操作符。它会先返回变量 i 的当前值,然后再将变量 i 的值加 1。在 for 循环中,使用 i++ 可以在每次迭代结束后递增 i 的值。

    示例:

    for (let i = 0; i < 5; i++) {
      console.log(i);
    }
    // 输出:0 1 2 3 4
    

上面这种情况下,i++与++i可以互换使用

有点区别的使用

  1. 表达式中使用:

    • i++:在表达式中使用 i++ 时,它会先返回 i 的当前值,然后再将 i 的值加 1。
    • ++i:在表达式中使用 ++i 时,它会先将 i 的值加 1,然后再返回递增后的值。

    示例:

      let i = 0;
      let a = i++;
     console.log(a)  // 0
     console.log(i)    // 1
     
     let b = ++i
     console.log(b )   // 2
     console.log(i )   // 2
    
  2. 与setTimeout一起使用
    (1)使用 i++:

for (let i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i); // 0 1 2 3 4 
  }, 1000); // 1000表示1000毫秒,即1秒
}

控制台的结果是依次输出0到4。
因为for循环头部的let不仅将i绑定到for循环中,事实上它将其重新绑定到循环体的每一次迭代中,确保上一次迭代结束的值重新被赋值。setTimeout里面的function()属于一个新的域,通过var定义的变量是无法传入到这个函数执行域中的,通过使用let来声明块变量能作用于这个块,所以function就能使用i这个变量了;这个匿名函数的参数作用域和for参数的作用域不一样,是利用了这一点来完成的。这个匿名函数的作用域有点类似类的属性,是可以被内层方法使用的。
注意:for循环从开始到结束的过程,需要维持几微秒或几毫秒,当定时器跑完一秒之后for循环早已经做完了。

当执行这段代码时,以下是大致的执行过程:

  • 初始化:创建一个变量 i 并将其初始值设为 0。

  • 判断条件:检查 i 是否小于 5。由于 i 的初始值是 0,满足条件,进入循环体。

  • 循环体:执行循环体中的代码。

    • 创建一个 setTimeout 函数,并将一个匿名函数作为回调函数传递给它。
    • 在匿名函数中,打印变量 i 的值。
    • setTimeout 函数会在 1000 毫秒(1秒)后触发回调函数的执行。
  • 延迟等待:在循环的每次迭代中,setTimeout 函数会在 1000 毫秒后触发回调函数的执行,但不会阻塞主线程的执行。因此,在每次迭代时,setTimeout 函数会被调用并设置一个计时器,计时器会在指定的延迟时间后触发回调函数的执行。

  • 迭代:循环体执行完毕后,i 的值会递增 1,然后再次进行条件判断。

  • 终止条件:当 i 不再小于 5 时,循环终止。

在这 1000 毫秒的延迟期间,主线程并不会等待 setTimeout 的回调函数执行完毕,而是继续执行循环。循环在执行完毕后,i 的值会递增为 5,此时循环终止。

当 1000 毫秒的延迟时间过去后,setTimeout 的回调函数开始执行,但此时循环已经结束,i 的值已经是 5。因此,在回调函数中打印的 i 的值都是 5。

这是由于 JavaScript 的事件循环机制决定的。事件循环会在主线程空闲时检查是否有异步操作需要执行,而不会阻塞主线程的执行。

而对于用var来声明变量时:

for (var i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i); // 5 5 5 5 5 
  }, 1000); // 1000表示1000毫秒,即1秒
}

( 2 ) 如果你希望在循环中按照预期输出 0 到 4 的值,可以使用闭包,例如:

for (let i = 0; i < 5; i++) {
  (function(j) {
    setTimeout(function() {
      console.log(j);
    }, 1000);
  })(i); // 0 1 2 3 4
}

在这个例子中,通过闭包,将i的变量驻留在内存中,当输出j时,引用的是外部函数的变量值i,i的值是根据循环来的,执行setTimeout时已经确定了里面的的输出了。

( 3 ) 但是再看看下面这种情况:

for (var i=0; i<5; i++) {
    (function() {
        setTimeout( function timer() {
            console.log( i );
        }, i*1000 );
    })(); // 5 5 5 5 5
}

首先运行外部的所有主程序,虽然for循环内形成了闭包,但是function并没有发现一个实参,所以仍然是连续输出5个5。

而下面这个程序与上面的程序又有什么区别呢?

function timer(i) {
    setTimeout( console.log( i ), i*1000 );
}
for (var i=0; i<5;i++) {
    timer(i);
}

根据代码逻辑,预期的输出应该是依次打印出数字 0 到 4,并且每个数字之间的打印间隔会逐渐增加,分别为 1 秒、2 秒、3 秒、4 秒和 5 秒。

然而,实际上,这段代码会立即打印出数字 0 到 4,而不会按照预期的延迟时间进行打印。

这是因为 setTimeout 函数需要接收一个函数作为参数,而你在调用 setTimeout 时直接传入了 console.log(i),这会立即执行 console.log(i) 并将其返回值作为参数传递给 setTimeout,而不是将 console.log(i) 作为回调函数。

为了实现预期的延迟打印效果,你可以将 console.log(i) 包装在一个匿名函数中,然后将该匿名函数作为回调函数传递给 setTimeout。如下所示:

function timer(i) {
    setTimeout(function() {
        console.log(i);
    }, i * 1000);
}

for (var i = 0; i < 5; i++) {
    timer(i);
}

通过将 console.log(i) 包装在匿名函数中,console.log(i) 不会立即执行,而是在定时器延迟时间到达后才执行,从而实现了预期的延迟打印效果。

  1. 使用 ++i:
for (let i = 0; i < 5; ++i) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}

在这个例子中,使用 ++i,在每次循环开始前,i 的值会先递增 1。在回调函数执行时,i 的值会是递增后的值。因此,输出的结果会是 0 到 4。

因此,在需要在循环中使用异步操作时,i++ 和 ++i 的使用是有区别的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值