for(var i = 0;i<3;i++){
setTimeout(function(){
console.log(i)
},1000)
};
打印结果:三个3
原因分析:setTimeout()是一个异步处理函数,它会等待所有的主线程任务处理完,才开始执行自己的内部的任务,每隔1s往任务队列中添加一个任务【闭包函数,setTimeout()中的函数,现在还没执行】,当主线程执行完时,这时i=3,
for循环括号内的就是主线程,执行完时i是3,所以会打印出3次3;
如果想打印出0,1,2
解决方案一:闭包
for (var i = 1; i <= 5; i++){
setTimeout(function timer(i) {
console.log(i)
}(i), 3000);
}
这样会发现打印出来的值是1, 2, 3, 4, 5;但是没有时间间隔就输出。这时因为在function timer()
后面添加()
表示的是立即执行函数,只要setTimeout
调用就执行了,而不是等到定时器到后才执行
for (var i = 1; i <= 5; i++) {
(function (j) {
setTimeout(function timer() {
console.log(j)
}, 3000);
})(i)
}
这样会发现打印出来的值是1, 2, 3, 4, 5;会等待3秒再同时输出;与上面不同,这次的setTimeout要等到定时器到后才能执行。
如果想要每次输出后间隔一秒,只需要将传入的 i 和 time 相乘即可:
for (var i = 1; i <= 5; i++) {
(function (j) {
setTimeout(function timer() {
console.log(j)
}, j * 1000);
})(i)
}
至于为什么是每隔一秒?因为按照上面写法,每隔setTimeout
的定时器依次是 1s,2s,3s,4s,5s。之前说过,定时器计时是相对于主线程运行完毕后开始的,所以相邻输出间隔1s.
为什么闭包可以解决该问题?因为函数是有作用域的。这也是解决方案二可以实现的原因。
解决方案二:拆分结构
function timer(i) {
setTimeout(() => {
console.log(i)
}, i *1000);
}
for (var i = 1; i <= 5; i++){
timer(i)
}
也是解决了 i 的作用域问题。输出也是每隔一秒输出。
解决方案三:let
let声明的变量只在其声明的块或子块中可用,这一点,与var相似。二者之间最主要的区别在于var声明的变量的作用域是整个封闭函数。
for (let i = 1; i <= 5; i++){
setTimeout(function timer() {
console.log(i)
}, i * 1000);
}
这样打印的效果也是每隔一秒打印一个数值:1, 2, 3, 4, 5