一道经典面试题-----setTimeout(function(){},0)
一道经典面试题-----setTimeout(function(){},0)
转载: http://www.w3cfuns.com/notes/17398/e8a1ce8f863e8b5abb530069b388a158/page/3.html#tagsbar
先看题:
1 for (var i = 0; i < 3; i++) { 2 setTimeout(function() { 3 console.log(i); 4 }, 0); 5 console.log(i); 6 }
结果是:0 1 2 3 3 3
很多公司面试都爱出这道题,此题考察的知识点还是蛮多的。
都考察了那些知识点呢?
异步、作用域、闭包。
我们来简化此题:
1 setTimeout(function() { 2 console.log(1); 3 }, 0); 4 console.log(2); //先打印2,再打印1
因为是setTimeout是异步的。
正确的理解setTimeout的方式(注册事件):
有两个参数,第一个参数是函数,第二参数是时间值。
调用setTimeout时,把函数参数,放到事件队列中。等主程序运行完,再调用。
就像我们给按钮绑定事件一样:
1 btn.onclick = function() { 2 alert(1); 3 };
这么写完,会弹出1吗。不会!!只是绑定事件而已! 必须等我们去触发事件,比如去点击这个按钮,才会弹出1。
setTimeout也是这样的!只是绑定事件,等主程序运行完毕后,再去调用。
setTimeout的时间值是怎么回事呢?
1 setTimeout(fn, 2000)
程序会不会报错? 不会!而且还会准确得打印1。为什么? 因为真正去执行console.log(i)这句代码时,var i = 1已经执行完毕了!
所以我们进行dom操作。可以先绑定事件,然后再去写其他逻辑。
1 window.onload = function() { 2 fn(); 3 } 4 var fn = function() { 5 alert('hello') 6 };
这么写,完全是可以的。因为异步!
es5中是没有块级作用域的。
1 for (var i = 0; i < 3; i++) {} 2 console.log(i); //3,也就说i可以在for循环体外访问到。所以是没有块级作用域。
这回我们再来看看原题。
原题使用了for循环。循环的本质是干嘛的?
是为了方便我们程序员,少写重复代码。
原题等价于:
1 var i = 0; 2 setTimeout(function() { 3 console.log(i); 4 }, 0); 5 console.log(i); 6 i++; 7 setTimeout(function() { 8 console.log(i); 9 }, 0); 10 console.log(i); 11 i++; 12 setTimeout(function() { 13 console.log(i); 14 }, 0); 15 console.log(i); 16 i++;
因为setTimeout是注册事件。根据前面的讨论,可以都放在后面。
原题又等价于如下的写法:
1 var i = 0; 2 console.log(i); 3 i++; 4 console.log(i); 5 i++; 6 console.log(i); 7 i++; 8 setTimeout(function() { 9 console.log(i); 10 }, 0); 11 setTimeout(function() { 12 console.log(i); 13 }, 0); 14 setTimeout(function() { 15 console.log(i); 16 }, 0); //弹出 0 1 2 3 3 3
怎么保证能弹出0,1, 2呢?
1 for (var i = 0; i < 3; i++) { 2 setTimeout((function(i) { 3 return function() { 4 console.log(i); 5 }; 6 })(i), 0); //改为立即执行的函数 7 console.log(i); 8 }