JS 函数的执行时机
为什么是6个6?
let i = 0
for(i = 0; i < 6; i++){
setTimeout(()=>{
console.log(i)
}, 0)
}
上面的代码,console.log(i)打印出的结果是6、6、6、6、6、6,并不是我们惯性理解的0、1、2、3、4、5。原因就出现在setTimeout()这个定时器上,该定时器在一段时间后,执行一个函数或者指定的一段代码。
- 先声明变量
i,赋值为0。 for循环:变量i初始值为0,满足条件i < 6,执行循环语句setTimeout(()=>{console.log(i)}, 0),意思是,过一会儿输出i的值。然后执行i++,i的值变为1。i = 1,满足条件i < 6,执行循环语句,过一会儿输出i的值。i = 2,满足条件i < 6,执行循环语句,过一会儿输出i的值。i = 3,满足条件i < 6,执行循环语句,过一会儿输出i的值。i = 4,满足条件i < 6,执行循环语句,过一会儿输出i的值。i = 5,满足条件i < 6,执行循环语句,过一会儿输出i的值。i = 6,不满足条件i < 6,跳出循环。- 此时一共累积了6条 “过一会儿输出
i的值”。 - 现在
i的值已经为6,最终输出6个6。
JavaScript 传统上是单线程的,此线程称为主线程(main thread)。一个线程是一个基本的处理过程,每个线程一次只能执行一个任务。每个任务顺序执行,只有前面的结束了,后面的才能开始。
所以单线程意味着所有任务需要排队,上例中,setTimeout()里面的console.log(i)便是在for循环整个完成后,才能开始执行。
但只用做如下的一点改动,便可以最终输出0、1、2、3、4、5。
for(let i = 0; i < 6; i++){
setTimeout(()=>{
console.log(i)
}, 0)
}
这两段代码的区别仅仅只有,第二段代码把let写在了for循环里,for(let i = 0; i < 6; i++){循环体}这句话的圆括号之间,有一个隐藏的作用域,在每次执行循环体之前,JS 引擎会自动把i在循环中重新声明及初始化一次。
这样通过6次循环便得到了我们理想中的0、1、2、3、4、5,这6个不同的i的值,等待for循环结束后,开始执行setTimeout(),输出0、1、2、3、4、5。
还有什么方法可以打印出0、1、2、3、4、5 ?
使用立即执行函数
let i = 0
for(i = 0; i < 6; i++){
!function(i){
setTimeout(()=>{
console.log(i)
},0)
}(i)
}
使用setTimeout()的第三个参数
let i = 0
for(i = 0; i < 6; i++){
setTimeout((i)=>{
console.log(i)
}, 0, i)
}
使用const关键字
let i
for(i = 0; i < 6; i++){
const x = i
setTimeout(()=>{
console.log(x)
})
}