JavaScript入门-- 函数的执行时机
js 的函数执行时机很重要,一不小心就容易踩坑,例如下面这两种情况:
情况一:
let i = 0
for(i = 0; i<6; i++){
console.log(i)
}
情况二:
let i = 0
for(i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
问两种情况打印出的 i 是多少?如果 js 基础不扎实就有可能踩坑。
答案是:
情况一:0、1、2、3、4、5 。
情况二:6、6、6、6、6、6。
情况二之所以与情况一不一样是因为 setTimeout 是延迟函数,js 在这种情况下需要先执行完 for 循环才能执行 setTimeout 函数的逻辑,而执行完 for 循环 i 的值就已经固定了,不会变,所以会打印出多个6。
解决办法:
想要解决问题,就要明白为什么会出现问题。
之所以产生上面的问题就是因为在 setTimeout 函数执行前 i 的值就已经固定了,如果想要情况二和情况一的行为相同那就想办法保留 i 在累加过程中的所有中间值,然后用这些中间值当作参数分别传入 setTimeout 函数中,所以常见办法有两种:
第一种:
for(let i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
将 let 关键字放在 for 循环内部,这样每次循环的时候 js 会帮我们把 i 的所有中间值都保留在内存中,当 for 循环执行完后再一个一个传入 setTimeout 函数中执行代码逻辑。
第二种:
let i = 0
for(i = 0; i<6; i++){
doLog(i)
}
function doLog(i){
setTimeout(()=>{
console.log(i)
},0)
}
这样为什么也行? 当然也是保存了 i 的所有中间值了,因为 doLog 不是延迟函数所以会立即执行,而当 doLog 函数被调用的时候会将函数按照顺序放到调用栈中,而函数的参数会被拷贝一份副本跟 doLog 函数一起压栈,所以也会避免情况二发生。
当然肯定不止有这两种方式,任何能够将 i 的中间值都保留下来的方式,都有可能是解决办法之一。