JavaScript回调中传递变量参数的正确方法?

JavaScript回调中传递变量参数的正确方法?,javascript,jquery,ajax,Javascript,Jquery,Ajax,我觉得这应该在互联网上的某个地方得到回答,但我没有找到它,可能是因为我没有搜索正确的术语,但问题是:我有以下功能: function ParentFunction (DataBase, Parameters) { for (k = 0; k < DataBase.length; k++){ var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid; $.aj

我觉得这应该在互联网上的某个地方得到回答,但我没有找到它,可能是因为我没有搜索正确的术语,但问题是:我有以下功能:

function ParentFunction (DataBase, Parameters) {            
  for (k = 0; k < DataBase.length; k++){
    var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid;
    $.ajax({
      url: CalendarURL,
      dataType: 'json',
      timeout: 3000,
      success: function( data ) { succesFunction(data, k, Parameters);},
      error: function( data ) { errorFunction ("Error",Parameters); }
    });
  }
}
函数父函数(数据库,参数){
对于(k=0;k
我在succesFunction(数据、k、参数)中遇到错误,因为“k”总是使用最新的值进行计算。所发生的情况是,当for循环运行k正确增加时,但是当执行回调函数successFunction时,通常在循环完成几毫秒后,总是使用最后一个值k,而不是调用$.ajax的循环的值来计算它

我通过创建另一个包含ajax调用的函数修复了这个问题。看起来是这样的:

function ParentFunction (DataBase, Parameters) {        
  for (k = 0; k < DataBase.length; k++){
    var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid;
    AjaxCall(CalendarURL, k, Parameters);
  }
}

function AjaxCall(URL, GroupIndex, Parameters) {
    $.ajax({
      url: URL,
      dataType: 'json',
      timeout: 3000,
      success: function( data ) { succesFunction(data, GroupIndex, Parameters);},
      error: function( data ) { errorFunction ("Error",Parameters); }
    });
}
var partial = (function() {
  var slice = Array.prototype.slice;
  return function(fn) {
    var args = slice.call(arguments,1);
    return function() {
      return fn.apply(this, args.concat(slice.call(arguments)));
    }
  }
}).call();
函数父函数(数据库,参数){
对于(k=0;k
它是有效的。我认为,当在parentFunction中调用函数时,会创建参数值的副本,当回调执行时,会看到这个值,而不是变量k,此时变量k的值会错误


所以我的问题是,这是实施这种行为的方式吗?还是有更合适的方法?我担心,不同的浏览器会采取不同的行动,使我的解决方案在某些情况下工作,而在其他情况下不工作

javascript遇到了一个常见问题:
var
变量是函数范围的,而不是块范围的。我将使用一个更简单的例子,重现同样的问题:

for(var i = 0; i < 5; i++) {
  setTimeout(function() { alert(i) }, 100 * i);
}
不过,这不是最漂亮或最容易阅读的。另一种解决方案是完全创建一个单独的函数:

for(var i = 0; i < 5; i++) {
  scheduleAlert(i);
}

function scheduleAlert(i) {
  setTimeout(function() { alert(i); }, 100 * i);
}
for(变量i=0;i<5;i++){
附表1(i);
}
功能调度警报(i){
setTimeout(函数(){alert(i);},100*i);
}

在不久的将来,当浏览器开始支持ES6时,我们将能够使用
let
而不是
var
,后者具有块范围语义,不会导致这种混淆。

这与javascript中的闭包有关。您的匿名函数都引用了当前作用域之外的变量,因此每个函数的“k”都绑定到原始循环变量“k”。由于这些函数在一段时间后被调用,因此每个函数都会回过头来查看“k”是否位于其最后一个值

最常见的解决方法就是你所做的。不要在嵌套函数定义中使用“k”(强制闭包),而是将其作为参数传递给不需要闭包的外部函数

以下是一些有类似问题的帖子:


另一种选择——而不是创建一个新的命名函数——是使用。 简单地说,部分应用程序是一个函数,它接受一个接受n个参数的函数,以及一个应该部分应用的m个参数,并返回一个接受(n-m)个参数的函数

左侧部分应用程序的简单实现如下所示:

function ParentFunction (DataBase, Parameters) {        
  for (k = 0; k < DataBase.length; k++){
    var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid;
    AjaxCall(CalendarURL, k, Parameters);
  }
}

function AjaxCall(URL, GroupIndex, Parameters) {
    $.ajax({
      url: URL,
      dataType: 'json',
      timeout: 3000,
      success: function( data ) { succesFunction(data, GroupIndex, Parameters);},
      error: function( data ) { errorFunction ("Error",Parameters); }
    });
}
var partial = (function() {
  var slice = Array.prototype.slice;
  return function(fn) {
    var args = slice.call(arguments,1);
    return function() {
      return fn.apply(this, args.concat(slice.call(arguments)));
    }
  }
}).call();
这样,您就可以使用需要两个参数的函数,如:

function add(a,b) { return a + b; }
转换为只需要一个参数的函数:

var increment = partial(add, 1);
increment(1);  // 2
increment(10); // 11
甚至是一个不需要修改的函数:

var return10 = partial(add, 5, 5);
return10(); // 10
这是一个简单的仅左侧部分应用程序函数,但是提供了一个版本,可以在参数列表中的任何位置部分应用参数

例如,您可以执行以下操作,而不是调用
AjaxCall()
来创建稳定的变量范围:

function ParentFunction (DataBase, Parameters) {            
  for (k = 0; k < DataBase.length; k++){
    var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid;
    var onSuccess = _.partial(succesFunction, _, k, Parameters);
    $.ajax({
      url: CalendarURL,
      dataType: 'json',
      timeout: 3000,
      success: onSuccess,
      error: function( data ) { errorFunction ("Error",Parameters); }
    });
  }
}
签字如下:

function(data) { /* work */ }
这是调用成功回调时实际使用的签名



尽管不可否认,对于已经描述的相同基础概念来说,这几乎只是语法上的甜点,但有时从功能角度而不是程序角度来思考这些问题在概念上会有所帮助。

就是这样。在未来我们将得到。良好的实践是:var i=0;对于(i;iAFAIK,
for
循环的初始化步骤只运行一次。