JS 函数的执行时机之setTimeout
setTimeout()的意思是设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码。
例:
如下代码会打出6个6。
let i = 0 for(i = 0; i<6; i++){ setTimeout(()=>{ console.log(i) },0) }
//打印结果6,6,6,6,6,6
其原因是因为:
setTimeout函数会优先执行之前的事件,最后再执行后续的事件。而之前的事件是i循环,显然得等i循环执行完毕再执行打印出i的这个函数,当i等于5时,因为小于6会再执行一次,直到i等于6,一共执行了6次,根据前面所诉会打出6个6,而非6个5。
不过,为了让上面的结果变成 0、1、2、3、4、5,可以使用如下的代码。
for(let i = 0; i<6; i++){ setTimeout(()=>{ console.log(i) },0) }
//打印结果0,1,2,3,4,5
在这个情况下,let关键字劫持了for循环的块作用域。通俗解释为JS在for和let一起使用的时候会添加东西,每次进入循环的时候会多创建一个i,把i复制一份留在这个空间用于打印,这个所谓的i不是会变的i,是变化之前的拷贝,不跟随变化的i变化,所以一共变化了5次,不会到6,打印出0、1、2、3、4、5。
这使得setTimeout毫无意义。
我还提供了2种方法可以打印出0、1、2、3、4、5的方法供大家参考。
1. 使用立即执行函数
for(var i = 0; i < 5; i++) { (function(i) { setTimeout(function () { console.log(i); }); })(i) } console.log('a');
//打印结果0,1,2,3,4,5
和上面有点类似,这个是利用闭包的原理,闭包使一个函数可以继续访问它定义时的作用域。而这个新生成的作用域将每一次循环的当前i值单独保存了下来。
2. 使用try...catch语句
for(var i = 0; i < 5; i++) { try { throw(i) } catch(j) { setTimeout(function () { console.log(j); }); } }
//打印结果0,1,2,3,4,5
这是因为try...catch语句的catch后面的花括号是一个块作用域,和let的效果一样。所以在try语句块里抛出循环变量i,然后在catch的块作用域里接收到传过来的i,就可以将循环变量保存下来,实现类似闭包和let的效果。
浙公网安备 33010602011771号