如何在循环中创建一个闭包,该循环捕获变量并在Javascript中获取参数?

How can you create a closure in a loop that which captures variables and takes parameters in Javascript?

我正在尝试为vis.js网络创建一个click事件处理程序,如下例所示:

1
2
3
4
5
network.on("click", function (params) {
    params.event ="[original event]";
    document.getElementById('eventSpan').innerHTML = 'Click event:' + JSON.stringify(params, null, 4);
    console.log('click event, getNodeAt returns: ' + this.getNodeAt(params.pointer.DOM));
});`

但是我试图在循环中这样做,并希望匿名事件处理函数能够在循环中使用局部变量(在连续迭代中更改它们之前)。

例如:

1
2
3
4
5
6
7
for (data in data_list) {
    var network = new vis.Network(data['container'], data['data'], data['options']);
    network.on("click", function(params) {
         console.log(data) // On event, data always equal to last element in data_list. I want it to save the data from the iteration this function was created in
         console.log(params) // I also need to access 'params'
    }
}

我知道这应该用一个闭包来完成,但是我找不到任何关于如何执行此操作的示例,同时还保持对传入的'params'的访问。如何创建一个可以接受'params'参数的闭包但是 还保存'数据'的状态?


我通常外化函数,传递我想要保留的数据并返回一个接受处理程序参数的函数:

1
2
3
4
5
6
7
8
9
10
11
function getOnClickHandler(data) {
   return function(params) {
      console.log(data);
      console.log(params);
   }
}

for (data in data_list) {
    var network = new vis.Network(data['container'], data['data'], data['options']);
    network.on("click", getOnClickHandler(data));
}

啊,这是一个循环的回调问题。

对于这种情况发生的原因的解释是,在收到单击事件之前,您的代码将完整地执行。它意味着它通过创建新网络,定义新的onClick处理程序,然后将它们添加到网络来循环。虽然这些处理程序中的每一个都是独立的,但它们都捕获相同的数据变量,这将在循环结束时最终引用最后一个数据元素。

1
2
3
4
5
var arr = [1, 2, 3, 4, 5]
for (i in arr) {
    setTimeout(function () { console.log(i) }, 1000);
}
// prints 4, 4, 4, 4, 4

有两种方法可以解决此问题。第一种方法是利用ES 6的let声明,它使变量成为块作用域(例如for循环体),vs var声明是函数作用域。

1
2
3
4
5
var arr = [1, 2, 3, 4, 5]
for (let i in arr) {  // NOTE THE USE OF let HERE.
    setTimeout(function () { console.log(i) }, 1000);
}
// prints 0, 1, 2, 3, 4

第二种方法是将var传递给一个函数,以便为每个循环代码创建一个新变量,该代码作用于该函数。

1
2
3
4
5
6
7
8
9
function handlerWrapper(i) {
    return function() { console.log(i) };
}

var arr = [1, 2, 3, 4, 5]
for (i in arr) {
    setTimeout(handlerWrapper(i), 1000);
}
// prints 0, 1, 2, 3, 4

通过这种安排,您的参数应该适用于您的回调函数:

1
2
3
function handlerWrapper(data) {
    return function(params) { console.log(data, params) };
}


1
2
3
4
5
6
7
for (data in data_list) {
    var network = new vis.Network(data['container'], data['data'], data['options']);
    network.on("click", (params) => {
         console.log(data) // On event, data always equal to last element in data_list. I want it to save the data from the iteration this function was created in
         console.log(params) // I also need to access 'params'
    }
}

如果您需要进一步澄清,请与我们联系。