没啥区别的使用
在 for 循环中,++i 和 i++ 都是用于递增变量 i 的操作符,但它们的使用方式略有不同。
-
++i:这是前缀递增操作符。
它会先将变量 i 的值加 1,然后返回递增后的值。在 for 循环中,使用 ++i 可以在每次迭代之前递增 i 的值。示例:
for (let i = 0; i < 5; ++i) { console.log(i); } // 输出:0 1 2 3 4
-
i++:这是后缀递增操作符。它会先返回变量 i 的当前值,然后再将变量 i 的值加 1。在 for 循环中,使用 i++ 可以在每次迭代结束后递增 i 的值。
示例:
for (let i = 0; i < 5; i++) { console.log(i); } // 输出:0 1 2 3 4
上面这种情况下,i++与++i可以互换使用
有点区别的使用
-
表达式中使用:
- 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
-
与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)
不会立即执行,而是在定时器延迟时间到达后才执行,从而实现了预期的延迟打印效果。
- 使用 ++i:
for (let i = 0; i < 5; ++i) {
setTimeout(function() {
console.log(i);
}, 1000);
}
在这个例子中,使用 ++i,在每次循环开始前,i 的值会先递增 1。在回调函数执行时,i 的值会是递增后的值。因此,输出的结果会是 0 到 4。
因此,在需要在循环中使用异步操作时,i++ 和 ++i 的使用是有区别的。