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

时间:2017-09-15 18:33:07

标签: javascript closures

我正在尝试为vis.js网络创建一个点击事件处理程序,如this example:

中显示的那样
network.on("click", function (params) {
    params.event = "[original event]";
    document.getElementById('eventSpan').innerHTML = '<h2>Click event:</h2>' + JSON.stringify(params, null, 4);
    console.log('click event, getNodeAt returns: ' + this.getNodeAt(params.pointer.DOM));
});`

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

例如:

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'
    }
}

我理解这应该通过闭包来完成,但是我找不到任何如何执行此操作的示例,同时还要保持对&#39; params&#39;的访问权限。传入。我怎么能创建一个可以接受&#39; params&#39;参数但也保存了数据&#39;?

的状态

3 个答案:

答案 0 :(得分:1)

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

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));
}

答案 1 :(得分:0)

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'
    }
}

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

答案 2 :(得分:0)

啊,在Javascript循环gotcha中的回调'。

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

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声明作为函数作用域。

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传递给函数,以便为每个循环代码创建一个新变量,该代码的作用域是该函数。

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

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

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