我们都知道JS的函数有许多关键的要素,如调用时机、作用域、形式参数、调用栈、函数提升等等。但当我们学习JS函数时,第一个需要明确的要素就是其调用时机,也即执行时机。我们先来看看以下这个例子:
let i = 0
for(i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
当我们执行以上函数时,JS会先声明一个 i
变量,并令它的值等于 0 ,然后依次执行 for
循环—— 先让 i
等于 0 ,然后判断 i
是否小于 6 ,若是,则执行 setTimeout
方法稍后打印出 i
的值,并且 i++
;若不是,则结束循环。
但是!
由于 setTimeout
方法的延迟性, i
的值并不会在每一次的 for
循环中打印出来,而是会等整个 for
循环结束之后再打印 i
的值。但是每执行一次 for
循环,就会有一个 i
的值会被打印,总共执行了 6 次,故会有 6 个 i
的值被打印出来。而每一次循环中 i
的值会稍后再打印,所以每次 i
被打印时它就已经执行了 6 次 i++
,所以打印出来的值将会是 6 个 6 。
那么我们有没有办法在不改变 for、let 组合的情况下,使函数依次打印出 0,1,2,3,4,5 呢?
当然可以。
我们只需要把 let
放进 for
循环中,那么就可以视作每次的 i
的值都是一次完整的函数执行,让 setTimeout
不用等整个 for
循环再执行。
for(let i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
那还有没有什么其他方法可以使函数依次打印出 0,1,2,3,4,5 呢?
有的,那就是加入一个立即执行函数让它能够保存每一次 i
的值,这样能够保证在跳出循环体后依然可以打印出之前保存的 i
的值。
let i = 0
for(i = 0; i<6; i++){
!function(j){ setTimeout(()=>{
console.log(j)
},0)
}(i)
}