var arr = [
   {time:2000, description:"123"},
   {time:5000, description:"456"},
   {time:8000, description:"789"},
];for(var i=0; i<arr.length; i++){
   setTimeout( function(){
     alert(arr[i].description);
   }, arr[i].time );
}我可以理解为,当i=3的时候,匿名闭包早已经访问了arr数组,arr没有i=3的,所以报错arr[i] is undefined但采用函数工厂模式,为其创造一个新的上下文环境就正常for(var i=0; i<arr.length; i++){
   setTimeout( function(a){
     return function(){
         alert(arr[i].description);
     }
   }(i), arr[i].time );
}如何解释清楚这个情况?

解决方案 »

  1.   

      setTimeout( function(){
      alert(arr[i].description);
      }
    , arr[i].time );红色部门可以看作是一个事件处理函数,类似与ajax回调函数,所以他不是立即执行
    而当他执行时,循环以结束
      

  2.   

      setTimeout( function(a){
      return function(){
      alert(arr[i].description);
      }
      }(i)
    , arr[i].time );函数执行会产生一个作用域,与外部无关,局部变量可在里面保存。 i为函数参数被传入,所以自然在里面保存
      

  3.   

    问下为什么当i=3的时候仍然会执行setTimeout里面的回调函数,不是i<arr.length,这里已经阻止了么?
      

  4.   


    因为for循环跑的快,在第三次运行setTimeout之前i已经变化了for循环是阻止了,但是i++没有阻止
      

  5.   

    这个我是知道了,只有i=3才可以阻止循环继续,但问题就是既然for循环是阻止了,那么循环体里面的匿名函数i只能是小3的数呀?当i=0的时候,循环体里面的i也是0;1的时候也是1。直到i=3了,for阻止了,那么就应该不在发生下去了,为什么function(){}里面的仍然可以找到i的?虽然此时i=3了,但i<3在function(){}之前,既然终止了循环继续,那么setTimeout里面的不发生,不发生就不可能找到i是3的值的啊?可能讲的有点乱,希望路过的高手能够耐心讲清楚,谢谢了!
      

  6.   


    var arr = [
      {time:2000, description:"123"},
      {time:5000, description:"456"},
      {time:8000, description:"789"},
    ];for(var i=0; i<arr.length; i++){
      setTimeout( function(){
          alert(i+' '+(arr[i]&&arr[i].description));
      }, arr[i].time );
    }
    /**
     * i=0:在两秒后执行alert(i+' '+(arr[i]&&arr[i].description))
     * i=1:在五秒后执行alert(i+' '+(arr[i]&&arr[i].description))
     * i=2:在八秒后执行alert(i+' '+(arr[i]&&arr[i].description)) * 每次执行setTimeout语句后i都递增,在i=2时执行了setTimeout语句后i=3
     * 就像5楼和2楼说的一样:
     * 1.在for语句块内,姑且把i变量的作用域看成for块。
     * 2.for执行的很快,当执行第一个alert时for已经遍历完毕,且i=3(执行setTimeout语句后i递增)
     */
    for(var i=0; i<arr.length; i++){
      setTimeout( function(a){
         return function(){
            alert(i+' '+(arr[i]&&arr[i].description));
         }
      }(i), arr[i].time );
    }
    /**
     * 这种情况和第一种类似,主要原因是变量i的作用域依然是for块,
     * 即使是return返回的函数中的变量i,它的作用域依然是for块,
     * 加入你把return返回的函数改变一下,改为:function(){
            alert(a+' '+(arr[a]&&arr[a].description));
         }
     * 即:
     */
    var arr = [
      {time:2000, description:"123"},
      {time:5000, description:"456"},
      {time:8000, description:"789"},
    ];for(var i=0; i<arr.length; i++){
      setTimeout( function(a){
          return function(){alert(a+' '+(arr[a]&&arr[a].description));}
      }(i), arr[i].time );
    }
    /**
     * 这时i的作用域姑且看成for块,而return返回的函数中的变量a(参数),它的作用域应该是setTimeout执行时的环境。
     * 变量由参数传递进去,这时在i=2时执行了setTimeout语句(执行完后i也递增了),传递进去后a为2,执行完setTimeout后i为3,检测i<arr.length,不成立,不再执行setTimeout语句,而此时setTimeout执行环境中用的变量是参数a=2,不在是for中的i=3,所以这个就正确、不出错
     */
      

  7.   

    这个错误跟闭包一毛钱关系都没有。。主要是你没理解setTimeout的机理。。
    你google下settimeout就会明白为什么会出错了,因为这些函数执行的时候,i的值为4
      

  8.   

    说错了,此时i的值为3,你可以运行下面的代码试试 for(var i = 0; i < 3; i++){
    setTimeout(function(){
    alert(i);
    }, 1);
    }
      

  9.   

    7楼说的并不完全正确,js是单线程执行的,而setTimeout是通过事件机制实现的,而事件机制呢又是以队列形式来调用的。。(说得有点乱,而且这里面的涉及浏览器原理一句两句肯定解释不清,你还是自己去搜资料吧),也就是说作为参数传给setTimeout的函数不管给的delay是多少都只会在当前代码执行完毕后才会调用,而不是for循环执行很快的问题。你再试试下面的代码
                for(var i = 0; i < 3; i++){
                    setTimeout(function(){
                        alert(i);
                    }, 0);
                }
      

  10.   

    唉~是和闭包有关系的,你的那个例子,1毫秒后,alert个4出来啊
      

  11.   


    嗯 你的问题我会keep住,仔细研究下
      

  12.   

    说for执行的快只是一种表象上的解释,幸好js没有线程休眠,不然真不知道怎么解释……对于这个问题,我继续保留我的意见。
    PS:一般处理这种错误我经常用的是函数委托。
      

  13.   

    哪里要这么复杂的区理解?
    我换种等价写法,马上就明白了,不过是js里面for语句没有自己的作用域罢了
    var i;
    for(i=0; i<arr.length; i++){
      setTimeout( function(){
      alert(arr[i].description);
      }, arr[i].time );
    }
    一下就清楚了撒,setTimeout时间到时,for循环已经执行完了,i=3;所以超出了索引
    这个写法和原写法是等价的,可以自己测试
    for(var i=0; i<3; i++){
      var test="test";
    }
    alert(i);//这儿会弹出3而不是undefind,说明i的作用域不是在for语句内
    alert(test);//这个也不会弹出undefind,因为test的作用域也不是for语句,for语句没有单独作用域
      

  14.   

    我说的是错误和闭包没关系,不是说这个例子和闭包没关系。。我取的例子并不是想说明和闭包没关系,而是和for执行快慢没关系,延迟0毫秒也是alert出4的
      

  15.   

    我说的是姑且把i的作用域放在for块里,这样比较容易解释一点。都知道js是没有块作用域的。