for循环+setTimeOut的经典面试问题
前段时间去面试的时候,遇到一道面试题,有关于for循环和setTimeOut的问题,其中还关乎var和let的区别
setTimeOut和setInterval的执行机制
在日常编码中,你会发现,给 setTimeout 和 setInterval 设定延迟时间往往并不准,或者干脆 setTimeout(function(){xxx},0) 也不是立马执行(特别是有耗时代码在前),这是因为 js 是单线程的,有一个事件队列机制,setTimeout 和 setInterval 的回调会到了延迟时间塞入事件队列中,排队执行。
setTimeout :延时 delay 毫秒之后,啥也不管,直接将回调函数加入事件队列。
setInterval :延时 delay 毫秒之后,先看看事件队列中是否存在还没有执行的回调函数( setInterval 的回调函数),如果存在,就不要再往事件队列里加入回调函数了。
如下例所示:
for(var i=0;i<3;i++){
setTimeout(function(){
alert(i);},1000)
}
结果:在一秒之后,同时输出3个3
因为 for 循环会先执行完(同步优先于异步优先于回调),这时五个 setTimeout 的回调全部塞入了事件队列中,然后 1 秒后一起执行了。
面试题
分别求下面两个代码块的执行结果
for(var i=0;i<3;i++){
setTimeout(function(){
console.log(i);
}
,1000)
}
结果:5 5 5 5 5
for(let i=0;i<5;i++){
setTimeout(function(){
console.log(i);
}
,1000)
}
结果:隔一秒输出,值分别为0 1 2 3 4
这是因为第一个代码块中setTimeout 的 console.log(i); 的i是 var 定义的,所以是函数级的作用域,不属于 for 循环体,属于 全局变量。等到 for 循环结束,i 已经等于 5 了,这个时候再执行 setTimeout 的五个回调函数(参考上面对事件机制的阐述),里面的 console.log(i); 的 i 去向上找作用域,只能找到 全局作用下 的 i,即 5。所以输出都是 5。
而let是代码块的作用域,即是局部变量,所以每一次 for 循环,console.log(i); 都引用到 for 代码块作用域下的i,因为这样被引用,所以 for 循环结束后,这些作用域在 setTimeout 未执行前都不会被释放。
块级作用域和函数作用域的区别