前端面试的一个小问题

昨天去一家公司面试前端,第二轮技术面给我出了一个题

var len=4;
while(len--){
    setTimeout(function(){
        alert(len);
    },0);
    alert(len);
}

问输出结果当时有点蒙,自己不熟悉setTimeOut函数以为time为0就是立即调用,然后考官说setTimeOut是异步的,即使是0也会在最后执行,结果输出应该为3,2,1,0,-1,-1,-1,-1.后来又变了题目是:

var len=4;
while(len--){
    (function(i){
        setTimeout(function(){
            alert(i);
        },0);
    })(len);
    alert(len);
}

这次我就很清楚了,产生一个闭包,每次len的变化i都保存了一个copy,所以输出是3,2,1,0,3,2,1,0.但是刚才写demo的时候发现第一段代码的输出符合预期,第二段的输出开始4个是3,2,1,0但是后面的也就是setTimeout中的输出有时候都不一样,有时是0,2,1,3有时是2,1,0,3等等求解释。

阅读 10.3k
5 个回答

前边那个你理解了?那我只说后边那个——闭包。

 (function(i){ setTimeout(function(){ alert(i); },0); })(len);

这里创建了一个函数并立即调用了,函数的参数 i 被设置成 len 现在的值,然后它就不改了。所以它 alert 的值是 0-3 而不是 -1。

至于顺序,都是延时 0 毫秒执行,所以顺序是乱的。

其实第二个题目的答案确实一定是:32103210

之所以后面四个 alert 输出 的结果有不确定性,setTimeout 以及延迟时间 都没有关系,是因为,alert 会阻塞浏览器的执行线程,而所有阻塞执行线程的方式,都会让setTimeout的结果具有不确定性。

(即便setTimeout的延迟时间为0,回调函数也是严格的压入队列栈中的,按照FIFO的顺序依次调用。)

比如,打开调试工具,设置断点,如果执行到延时回调函数中断点生效了,这时,代码中的 setTimeout setInterval 等结果也都会受到影响。

遇到这种情况,最好的方式(比如此处),尽量使用【console.log】输出结果,这样只会将对象打印到控制台,而不会影响时间性和顺序。

这个问题好奇怪,我把两段代码都放到 firefox 控制台里去执行,用 alert 还有把 alert 换成 console.log 都试了,两个结果都跟你的不一样耶,而且本身的结果也是变化的,我都有点糊涂了

最初,我个人以为timer callback也是依照队列顺序排在event loop队列中进行处理的。但是那个alert给出的结果在chrome下都不如我愿啊!然后我就把这个理论暂时给局部修正了。

看了玉溪童鞋的回复,大吃一惊啊。alert这货欺骗人啊!

话不多说了,我也上一个具有说服力的例子吧,把楼主给的示例改下。

为了证明timer callback也被压入队列并按先后顺序而不是delay time来执行的,我加入了 new Date().getMilliseconds()来随机控制delay time。

代码如下:

var test = function() {
    var len = 4;
    while (len--) {
        (function(len){
            setTimeout(function() {
                console.log('setTimeout log---' + len);
            }, new Date().getMilliseconds());
        })(len);
        console.log('log--'+len);
    }
};
test();

前面的因为settimeout是异步的当while循环结束len值为-1,所以同步会打出3,2,1,0,settimeout会打出-1,-1,-1,-1
后面的情况是利用了闭包原理,每次传入的len值为循环时进入的值,会传入settimeout中缓存住,所以输出3,2,1,0,3,2,1,0

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏