search

Home  >  Q&A  >  body text

各大互联网公司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

迷茫迷茫2818 days ago366

reply all(7)I'll reply

  • 巴扎黑

    巴扎黑2017-04-10 15:08:54

    闭包的事情我就不多做解释了,其它人说的很多了,关于 "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

    reply
    0
  • 高洛峰

    高洛峰2017-04-10 15:08:54

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

    reply
    0
  • PHP中文网

    PHP中文网2017-04-10 15:08:54

    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的最终值。

    reply
    0
  • 大家讲道理

    大家讲道理2017-04-10 15:08:54

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

    reply
    0
  • 黄舟

    黄舟2017-04-10 15:08:54

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

    reply
    0
  • PHP中文网

    PHP中文网2017-04-10 15:08:54

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

    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)
    }
    

    reply
    0
  • 高洛峰

    高洛峰2017-04-10 15:08:54

    不是setTimeout方法还可以向延迟函数传递额外参数的功能吗(尽管IE9及其之前的不兼容)。

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

    reply
    0
  • Cancelreply