JS循环中使用定时器的坑

JS循环中使用定时器的坑

执行以下代码:

1
2
3
4
for(let i=1; i<=5;i++){
console.log('ffff:'+i);
setTimeout(()=>{ console.log('ssss:'+i) },100);
}

结果:

1
2
3
4
5
6
7
8
9
10
ffff:1
ffff:2
ffff:3
ffff:4
ffff:5
ssss:1
ssss:2
ssss:3
ssss:4
ssss:5

执行以下代码:

1
2
3
4
for(var i=1; i<=5;i++){
console.log('ffff:'+i);
setTimeout(()=>{ console.log('ssss:'+i) },100);
}

结果:

1
2
3
4
5
6
7
8
9
10
ffff:1
ffff:2
ffff:3
ffff:4
ffff:5
ssss:6
ssss:6
ssss:6
ssss:6
ssss:6

唯一的区别: for里面定义变量,一个用的let,一个用的var。
let:函数作用域,在当前函数内可以使用。
var:全局作用域,定义之后全局可用。

还涉及一个知识点:context! 俗称上下文(虽然这么翻译很狗屎)

通俗点讲上下文,其实就是你当前执行环境的一个快照(也有是引用的情况)!

第一个例子:
使用let定义的变量,那么let的作用域仅仅在for里面生效,执行输出之后,执行setTimeout(异步),JS异步相当于添加一个任务队列,当同步任务执行完毕之后就会去执行异步队列的任务(这也解释了为什么有时候使用JS定时器不会按照你规定的时间去完成),添加异步任务的时候,保存了上下文,上下文中的i第一次为1,之后递加,总共循环5次,添加5个异步队列。

第二个例子:
使用var定义的变量,全局都有效,指向的是一个内存地址,无论哪里修改了,其他地方使用这个变量都会被修改(引用传递,内存地址只有一个),当第一次添加一个异步队列的时候,上下文指向的是同一个内存地址的i,随着循环的递加,i的地址不变,值每次都+1,那么当循环执行完毕,开始执行异步队列的时候,i的值取到的是 5++;for里面循环结束为最后一个表达式的值,这也就解释了为什么每次都是6.

支持技术分享,觉得有用可以请我喝杯咖啡!