JavaScript: 将局部变量传递给动态创建的函数,该函数是另一个函数内的参数。

3

我正在使用一个具有以下参数化的函数(无法更改):

my_function(data, callback_function(results, status) {});

我需要向回调函数传递额外的信息,这些信息无法添加到"data"(回调函数使用),"results"或"status"中。具体来说,这些信息是my_function调用所在的for循环计数器。

为了实现这一点,我在callback_function的主体中包含对计数器的引用:

for(var i = 0; i < 10; i++) {

  var data = 'cannot modify this data';

  my_function(data, function (results, status) { alert(i); });

}

不幸的是,最终值i(在这种情况下为9)被打印了10次。预期行为是打印循环中每个i的值(从0到9)。

动态函数能否访问其作用域内但范围之外的变量?


你已经在使用闭包了,我想.. 值9是对i的引用--在循环结束时被更改为9,因此总是打印9! - Vivek Chandra
如果你试图使用i提供的值来定义myfunction,并尝试稍后调用它 - 这是一个闭包..!!..它正在访问对i的引用.. - Vivek Chandra
@VivekChandra 我的代码的第二部分是对my_function的实际调用,而不是定义。我认为my_function不是闭包,因为i没有作为参数传递。 - kanoko
@Vivek:在JS中,整数不是引用类型。此外,他没有创建闭包。 - Niklas B.
3个回答

6

您需要创建一个闭包,其中包含匿名函数创建时i的值。您可以使用包装函数来实现:

function createClosure(x, func) {
  return function(results, status) { func(x, results, status); }
}

/* ... */

for(var i = 0; i < 10; i++) {
  var data = 'cannot modify this data';
  my_function(data, createClosure(i, function(i, results, status) { 
    alert(i);
    alert(results);
    alert(status);
  }));
}

如果您想简短一点,可以在原地创建闭包:

for(var i = 0; i < 10; i++) {
  var data = 'cannot modify this data';
  my_function(data, (function(i) {
    return function (results, status) { 
      alert(i); 
    }
  })(i));
}

很遗憾,我无法更改或使用作为参数传递的回调函数的参数。 - kanoko
@user1193461:我不明白?也许你想在循环内定义回调函数,在这种情况下,你需要额外的间接层... 我更新了代码,也许现在更有帮助了。 - Niklas B.
抱歉,我无法修改my_function的参数或callback_function的参数。您的回调函数“createClosure”不再使用所需的“results”和“status”参数。 - kanoko
@user1193461:你有实际尝试过吗?createClosure不是回调函数!它仅为您创建一个回调函数并返回它。我添加了另一个示例,它做同样的事情,但方式更加复杂。也许您会更喜欢它。 - Niklas B.
抱歉,这两个解决方案都是可行的。您能否解释一下第二种解决方案中以下语法的含义:(function(i) { //code })(i) - kanoko
由于浏览器兼容性,我会选择这个解决方案。谢谢! - kanoko

1
你需要的是Function.prototype.bind。使用它,你可以将一个函数绑定到特定的参数上。有了这个,你的代码会像这样:
for(var i = 0; i < 10; i++) {

  var data = 'cannot modify this data';

  function callback(iValue, results, status) { alert(iValue); }
  my_function(data, callback.bind(null, i)); // |null| since you don't seem to need any specific |this|.

}

我也喜欢这个。.bind在非Mozilla浏览器上支持吗? - kanoko
它在Chrome 18上得到支持(也许更早)。它是基于V8的,所以不确定Safari/JSC是否支持。至少,我提供的MDN链接包含了对不支持此方法的浏览器的实现。 - Alexander Pavlov
似乎在iPhone的Safari上,.bind不起作用。至少这个例子不行。如果可能的话,我想避免添加回退代码。希望苹果未来能解决这个问题。 - kanoko

0
for(var i = 0; i < 10; i++) {

var data = 'cannot modify this data';

my_function(data, function (results, status) { 
 alert( 
  function(value){ 
    return value;
    }(i);
 )
}   //function (results, status)  ends here
);  // myfunction ends here..

}

没有测试过,但是试一下吧,希望能够成功。

查看这个链接 -> Javascript closures - variable scope question 以更好地理解你正在做的事情。


你能解释一下这里x发生了什么吗?function(value) { return value; } (x); - kanoko
function(value) {...} 是一个匿名函数表达式。它可以像普通函数一样使用,但没有名称。您可以将其分配给变量(例如 window.onerror = function(e) { /* handle e */ },它将在内部被调用为 window.onerror()),或者您可以直接调用它,就像上面的例子一样。发生的事情是 value 参数接收到 x 的值。就是这样。 - Alexander Pavlov
我明白了,所以(x)被视为“函数”参数的方式与处理命令行参数的方式非常相似。这种将值传递到函数中的方式有一个名称吗? - kanoko
你可能想要查看ECMA-262规范,但我并没有看到fn(x)function(param) {...}(x)之间有太大的区别,后者只是直接调用函数而不是通过引用。 - Alexander Pavlov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接