for循环中的setTimeout的几种情况
在做js相关的题目时,经常会遇到以下几种容易混淆的setTimeout相关的题目:
第一种
for(var i=0;i<10;i++){
setTimeout(console.log(i),0);
}
在这个代码片段中,setTimeout 的第一个参数是 console.log(i),这是一个立即执行的函数调用,而不是一个回调函数。因此,console.log(i) 会立即执行,并且 setTimeout 的第二个参数 0 实际上没有任何意义,因为 console.log(i) 已经立即执行了。
所以,这个代码片段实际上等同于:
for(var i=0;i<10;i++){
console.log(i);
}
let同理
第二种:
for(var i=0;i<10;i++){
setTimeout(function(){
console.log(i);
},0);
}
在这个代码片段中,setTimeout 的第一个参数是一个匿名函数 function(){ console.log(i); },这是一个回调函数。这个回调函数会在 setTimeout 的延迟时间(这里是 0 毫秒)之后执行。
由于 var 声明的变量 i 是函数作用域(在 ES5 及之前),而不是块级作用域,所以在 for 循环结束后,i 的值是 10。当 setTimeout 的回调函数执行时,它们都会引用同一个 i,而这个 i 的值已经是 10。
第三种:
for(var i=0;i<10;i++){
setTimeout('console.log(i)',0); //10个10
}
循环执行:for 循环从 i=0 开始,每次循环 i 递增 1,直到 i<10 不成立为止。在每次循环中,都会调用 setTimeout,并传入一个字符串 ‘console.log(i)’ 作为回调函数,延迟时间为 0 毫秒。
字符串作为回调函数:当 setTimeout 的第一个参数是一个字符串时,JavaScript 会将其视为一个代码字符串,并在延迟时间结束后使用 eval 来执行这个字符串。这意味着在延迟时间结束后,eval 会执行 console.log(i),但此时 i 的值已经是 10,因为循环已经结束。
let同理
第四种:
for(let i=0;i<10;i++){
setTimeout(function(){
console.log(i);
},0);
}
循环执行:for 循环从 i=0 开始,每次循环 i 递增 1,直到 i<10 不成立为止。在每次循环中,都会调用 setTimeout,并传入一个匿名函数 function(){ console.log(i); } 作为回调函数,延迟时间为 0 毫秒。
变量作用域:使用 let 声明的变量 i 具有块级作用域。这意味着每次循环都会创建一个新的 i,并且每个 i 的作用域仅限于当前循环迭代。
回调函数执行:由于 let 具有块级作用域,每次循环都会创建一个新的 i,因此每个回调函数都会捕获到对应的 i 值。
当每个回调函数执行时,console.log(i) 会输出当前循环迭代对应的 i 值。