先看以下例子:
let i = 0
for(i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
这个例子的结果是:打印出 6 个 6。
来分析下它的结果:
- 首先:setTimeout() 函数的意思是过一会再执行,在这个例子里,会过一会再打印出 i 的值。显然,for 循环会让 setTimeout() 执行6次,结果会打印6次 i 的值。
- 可为什么6次取的 i 值都是 6,而不是 0,1,2,3,4,5 呢?
这是因为JS的闭包,是先记录“变量名”,等需要用到的时候再去取“变量值”。 for 循环每执行一次,都有一个 setTimeout() 记录要调用外部变量 i ,但每次只记录变量名为 i 而不取值;for 循环结束后(i = 6),setTimeout() 才去取出外部变量 i 的值。每次 setTimeout() 打印出一个6,故6次 setTimeout() 共打印出6个6 。 - 那为什么for 循环结束了,setTimeout() 才会去取外部变量的值?
参考这位同学的博客:JS 函数的执行时机
简单来说就是:JS是单线程环境,会优先执行完所有的同步任务(如 for 循环),再执行异步任务(for循环每执行一次,会把一个 setTimeout() 插入异步任务队列),因此 for 循环全部结束,才会从任务队列中取出 setTimeout() 来执行。
那么,怎么让它输出0,1,2,3,4,5呢?
可以 for 和 let 配合,let 声明放 for 循环里面:
for(let i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
除了 for 和 let 配合,还有什么方法呢?
方法1:放弃setTimeout() 函数,直接输出
let i = 0
for(i = 0; i<6; i++){
console.log(i)
}
此外,参考其他同学的博客,有:
方法2:让每次 setTimeout() 调用的 i 变量均为独立的变量。
- 比如用 JS 独有的立即执行函数:
let i=0
for(let i=0;i<6;i++) {
! function(i){
setTimeout(()=>console.log(i))
}(i)
}
- 比如限定 i 的作用域,使它成为局部变量:
可以通过把 setTimeout() 放进其他函数,再把 i 作为这个函数的形式参数
function log(j){
setTimeout(function(){
console.log(j);
},0);
}
let i=0;
for(i = 0; i<6; i++){
log(i);
}
方法3:利用setTimeout()函数的第三个参数
let i
for(i = 0; i<6; i++){
setTimeout((value)=>{
console.log(value)
},0,i)
}
将 i 的值传入到函数中,那么之后就是输出 i 当时传进来时候的值,并不会跟随她之后的值而改变。
方法4:当然,也可以引入另外一个变量temp,用 temp 保存 i 的值
let i
for(i = 0; i<6; i++){
let temp = i
setTimeout(()=>{
console.log(temp)
})
}