2012-01-11 66 views
2

我在循环中遇到setTimeout中的函数调用问题。
传递给函数的参数是循环中为每次迭代计算的最后一个值,请参阅下面的示例。

Javascript:setTimeout中的函数和参考

for(var i=0; i<datesYM.length; ++i) { 
    console.log(datesYM[i]); 
    var dateYM = datesYM[i]; 
    setTimeout(function() { 
     myDB.markMonthsValuesAsUpdated2(myDoctorId, dateYM) 
    }, Math.floor(Math.random()*5001)); 
} 

myDB.markMonthsValuesAsUpdated2 = function(myDoctorId, dateYM) { 
    console.log(dateYM); 
    [...] 

打印:

2012-01
2012-02 2012-03
2012-04

2012-05 2012-06
2012-07


2012-07
2012-07
2012-07
2012-07 2012-07

2012-07 2012-07

回答

5

包裹身体在它自己的自动执行功能,以强制执行新范围:

for(var i=0; i<datesYM.length; ++i) { 
    console.log(datesYM[i]); 
    var dateYM = datesYM[i]; 
    (function(dateYM) { 
     setTimeout(function() { 
      myDB.markMonthsValuesAsUpdated2(myDoctorId, dateYM) 
     }, Math.floor(Math.random()*5001)); 
    })(dateYM); 
} 

没有这一点,在循环中创建的每个函数都在的相同实例上关闭,其中dateYM具有最后一次迭代的值,但是这些函数中的任何一个函数都会执行。由于JavaScript具有函数作用域,所以包装器函数在每次迭代时创建一个新的dateYM,以便传递给setTimeout的每个新函数都有其自己的实例。

1

通过创建并返回超时处理程序的函数创建变量作用域。

function create_timeout_handler(myDoctorId, dateYM) { 
    return function() { 
     myDB.markMonthsValuesAsUpdated2(myDoctorId, dateYM) 
    }; 
} 

然后调用该函数并将它传递给任何需要作用域的东西。

setTimeout(create_timeout_handler(myDoctorId, dateYM), Math.floor(Math.random()*5001)); 

这是因为每次你都给人以setTimeout功能在同一个变量的作用域被创建的,所以他们都引用相同dateYM变量,这是正在循环覆盖。

传递dateYM到,创建并返回处理程序确保每个处理程序引用dateYM变量是独特的的create_timeout_handler每次调用另一个函数。