将额外参数传递给jQuery getJSON()成功回调函数

Chr*_*ris 21 javascript jquery json

我之前从未使用过回调函数,所以我可能犯了一个完全愚蠢的错误.我想我有点理解这里的问题,但不知道如何解决它.

我的代码(稍微简化)是:

for (var i = 0; i < some_array.length; i++) {
    var title = some_array[i];
    $.getJSON('some.url/' + title, function(data) {
        do_something_with_data(data, i);
    }
Run Code Online (Sandbox Code Playgroud)

据我所知,只有getJSON()收到数据时才会调用此匿名函数.但到目前为止,i没有我需要的价值.或者,就我的观察而言,它具有循环完成后它将具有的最后一个值(不应该超出界限吗?).

因此,如果数组的大小为6,do_something_with_data()则将使用值5调用五次.

现在我想,只是传递i给匿名函数

function(data, i) { }
Run Code Online (Sandbox Code Playgroud)

但这似乎不可能.现在不确定.

Eri*_*ikE 45

你需要了解一个闭包是什么.在javascript中,当你在一个函数内部使用一个在外部上下文(外部函数或全局函数)中定义的变量时,你会在该变量周围创建一个闭包,它会保持变量的实例化,并让函数每次都继续引用它.被调用(以及项目上有闭包的任何其他函数实例).

因为原始变量仍然是实例化的,所以如果在代码中的任何位置更改该变量的值,当函数稍后运行时,它将具有当前更改的值,而不是首次创建函数时的值.

在我们解决关闭工作正确之前,请注意title在循环中重复声明变量不起作用(实际上,您可以将变量视为基本上被提升function范围内 - 与其他语言不同,for循环在JavaScript没有作用域,因此变量仅为函数声明一次,并且不在循环内声明或重新声明).在循环外声明变量应该有助于澄清为什么代码不能按预期工作.

同样,当回调运行时,因为它们对同一个变量有一个闭包i,它们在i增量时都会受到影响,并且它们都将使用它们运行时的当前i(这将是你发现的错误,因为回调运行之后循环已完全创建回调).异步代码(例如JSON调用响应)在所有同步代码完成执行之前不会运行也无法运行 - 因此保证在执行任何回调之前完成循环.

要解决这个问题,你需要一个新的函数来运行它有自己的作用域,这样在循环内部声明的回调中,每个不同的值都有一个新的闭包.您可以使用单独的函数执行此操作,或者只在callback参数中使用调用的匿名函数.这是一个例子:

var title, i;
for (i = 0; i < some_array.length; i += 1) {
    title = some_array[i];
    $.getJSON(
       'some.url/' + title,
       (function(thisi) {
          return function(data) {
             do_something_with_data(data, thisi);
             // Break the closure over `i` via the parameter `thisi`,
             // which will hold the correct value from *invocation* time.
          };
       }(i)) // calling the function with the current value
    );
}
Run Code Online (Sandbox Code Playgroud)

为清楚起见,我将其分解为一个单独的函数,以便您可以看到正在发生的事情:

function createCallback(item) {
   return function(data) {
      do_something_with_data(data, item);
      // This reference to the `item` parameter does create a closure on it.
      // However, its scope means that no caller function can change its value.
      // Thus, since we don't change `item` anywhere inside `createCallback`, it
      // will have the value as it was at the time the createCallback function
      // was invoked.
   };
 }

var title, i, l = some_array.length;
for (i = 0; i < l; i += 1) {
    title = some_array[i];
    $.getJSON('some.url/' + title, createCallback(i));
    // Note how this parameter is not a *reference* to the createCallback function,
    // but the *value that createCallback() returns*, which is itself a function.
}
Run Code Online (Sandbox Code Playgroud)

注意:由于您的阵列显然只有标题,您可以考虑使用title变量而不是i要求您返回some_array.但无论哪种方式有效,你都知道自己想要什么.

考虑这个回调-创建功能(或者匿名的一个或一个潜在有用的方式createCallback之一)在本质上转换所述的i可变到单独的thisi变量,通过每次将有自己的范围的新函数.也许可以说"参数打破了封闭的价值".

请注意:由于对象是引用类型,因此该技术不会在不复制它们的情况下对对象起作用.仅仅将它们作为参数传递将不会产生事后不能改变的东西.您可以复制街道地址,但这不会创建新房.如果你想要一个能够带来不同东西的地址,你必须建造一所新房子.


pat*_*rjk 6

您可以使用立即函数(一个立即执行的函数)创建一个闭包,它返回另一个函数:

for (var i = 0; i < some_array.length; i++) {
    var title = some_array[i];
    $.getJSON('some.url/' + title, (function() {
        var ii = i;
        return function(data) {
           do_something_with_data(data, ii);
        };
    })());
}
Run Code Online (Sandbox Code Playgroud)