6个6
let i
for(i=0;i<6;i++){
setTimeout(()=>console.log(i),1000)
}
这段代码最后会打印出6个6.
i=0
: setTimeout(console.log(i),1000)
,1s后,打印i。有人说,现在i=0啊,为什么不是打印0?因为现在 console.log
这个函数还没有执行,只是定义。没执行的时候,js不会去管里边的内容。
i=1
:setTimeout(console.log(i),1000)
,1s后,还是打印i,而不是1。
一直到 i=5
。然后 i=6
,跳出循环。for
循环结束了,看看这时到没到1s,如果没到,那就不会去打印。到了1s了,而且现在还不忙,那就去执行函数。
如果后续函数用时很长,那就会在后续函数执行完后再去执行
setTimeout
,即setTimeout(fn(),1000)
表示1000ms后尽快执行,也就说他只限制了1000ms内不可以执行。
执行的函数是:console.log(i)
,执行时查看i
是几。现在i=6
,那就打印6。
后续还有五个函数,console.log(i)
,i
还是6,那就一直打印6.
所以最后的结果就是,打印6个6。
总结:函数在调用的时候才会执行,执行时才会看变量值。
怎么打印0,1,2,3,4,5?
之所以会打印出6个6 ,是因为函数没执行的时候,不会去看里边的值。i
从0到1,0这个值就没了,以此类推,中间的值都没了。
那我们把中间的值保存下来,打印这个保存下来的值不就好了。
let i
for(i=0;i<6;i++){
let j=i
setTimeout(console.log(j),1000)
}
i=0
: let j=0 ,setTimeout(console.log(j),1000)
1s后,打印j
i=1
: let j=1 ,setTimeout(console.log(j),1000)
1s后,打印j
有人说,i=1
的时候,j
不是变成1了吗,那第一个打印j
,打印出来的不就是新的值了吗?
注意,let
是有作用域的,它的作用域是从 {
开始,到 }
结束。也就是说,每次循环,都是一个新的作用域,在每个新的作用域里,j
其实是不同的,互不干扰的。
在 i=0
时,第一个作用域里,j=0
,那就一直是0。
在 i=1
时,又是一个新的作用域了,在这个新的作用域里,j=1
,那就一直是1。
其实本质就是 j1,j2,j3
,是不同的。
所以最后会打印出0,1,2,3,4,5
语法糖
因为其实第二种才是正常的可以理解的结果,所以js直接给了一个语法糖,你不用自己写 let j=i
这句话了,在for循环的 i
前边加一个 let
,就会有同样的效果。
for(let i=0;i<6;i++){
setTimeout(console.log(i),1000)
}
结果:打印0,1,2,3,4,5
加一个let
,就会在每次进入循环后,自动为我们创建一个隐形的i'
,就像j
一样,打印的其实是这个i'
,它会保存下中间的值
立即执行函数
除了用 let
保存中间值外,还有第二种方法,就是立即执行函数。
var
没有作用域。
每次循环,马上执行一个匿名函数,把每次的 i
作为实参,传给它的形参。因为这个匿名函数执行了,所以它里边的 j
(j叫i也可以,最前面的括号是形参,最后面的是实参) 就确定了值,就是每次的0,1,2,3,4,5。所以 for
循环结束以后,就会打印出已经定好的0,1,2,3,4,5