各大互联网公司2014前端笔试面试题–JavaScript篇

24.看下面代码,给出输出结果。

javascript for(var i=1;i<=3;i++){
   setTimeout(function(){
       console.log(i);    
   },0);  
 };

答案:4 4 4。

原因:Javascript事件处理器在线程空闲之前不会运行。那么问题来了,如何让上述代码输出1 2 3? //原因这句话是什么意思呀?

javascriptfor(var i=1;i<=3;i++){
   setTimeout((function(a){  //改成立即执行函数
       console.log(a);    
   })(i),0);  
};

7 1 //输出
8 2
9 3

阅读 7.1k
7 个回答

闭包的事情我就不多做解释了,其它人说的很多了,关于 "Javascript事件处理器在线程空闲之前不会运行" 这句话的意思我说一下吧。因为 JS 是单线程的,而 setTimeout 注册事件是队列的,setTimeout 放到队列之后,因为之前的 for 循环并没有执行完,所以不会立即执行 setTimeout 的。这也就是为什么说 JS 时间处理在线程空闲之前不会运行 的原因了。实际上 for 循环是这么走的:

  • 循环开始 i=0
  • 定义了一个 setTimeout 1 并放到队列中
  • 上一个循环结束 i=1
  • 定义了一个 setTimeout 2并放到队列中
  • 上一个循环结束 i=2
  • 定义了一个 setTimeout 3并放到队列中
  • 上一个循环结束 i=3
  • 定义了一个 setTimeout 4并放到队列中
  • 执行 setTimeout 1
  • 执行 setTimeout 2
  • 执行 setTimeout 3
  • 执行 setTimeout 4

使用闭包,把每一个i都驻留在内存里。

setTimeout会将函数放入队列,等待有机会让其执行。

队列:
 - i++  // 2
 - i++  // 3
 - i++  // 4
 - console.log(i) // 因为i 已经是4了,所以输出4

setTimeout:

setTimeout(function(){
console.log(i);
},0);

虽然函数等待了0秒,但是其实真正等到setTimeout中的匿名函数执行的时候,for循环的i已经从1变成了4。
所以得到的是4。

解决方法:通过闭包保存i,使之与匿名函数的i关联。

队列:
 - i++   // 2
 - (function(a){console.log(a)})(i)  // 末尾的括号中传入i,保存现在i的值 2
 - i++   // 3
 - (function(a){console.log(a)})(i)  // 末尾的括号中传入i,保存现在i的值 3
 - i++   // 4
 - (function(a){console.log(a)})(i)  // 末尾的括号中传入i,保存现在i的值 4

等真正执行匿名函数时,i就是每个函数保存的值,而不是for循环i的最终值。

直接把timeout删掉,就留着console那行就行了

Javascript是单线程运行的,在for循环及其所在的函数执行完毕前,setTimeout是没有机会执行的,就如 楼上 叙述,setTimout只是把需要执行的函数放在执行队列中等待排队执行而已

额,看到你的解决方法...

for(var i=1;i<=3;i++){
   setTimeout((function(a){  //改成立即执行函数
       console.log(a);    
   })(i),0);  
};

很有问题啊,是标准答案?

如果是用这个方法,setTimout的存在一点必要都没有了,因为里面的console.log(a);会马上执行,而不是setTimout的序列等待结束之后才执行,你可以把时间设成5秒,看看,是不是马上就打印了。

改成这样会好一些

for(var i=1;i<=3;i++){
   (function(a){
        setTimeout(function(){  //改成立即执行函数
           console.log(a);    
       },5000);  
    })(i)
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏