如何在javascript事件处理程序中获取对象?

时间:2011-01-19 03:16:15

标签: javascript event-handling closures

我试图在onclick事件处理函数中获取一个对象。

但它没有像我期望的那样发挥作用。

例如,如果我运行此代码:

var entries = [{id: 1},{id: 2},{id: 3}];

for (var i = 0; i < entries.length; i++) {

    var entry = entries[i];

    document.getElementById(entry.id).onclick = function () { 
        console.log("this.id: " + this.id);
        console.log("entry.id: " + entry.id);
    };

}

我的期望是:

this.id: 1
entry.id: 1

this.id: 2
entry.id: 2

this.id: 3
entry.id: 3

但我得到的是:

this.id: 1
entry.id: 3

this.id: 2
entry.id: 3

this.id: 3
entry.id: 3

为什么条目对象始终是id为3的条目?

如何在click事件处理程序中获取正确的条目对象?

3 个答案:

答案 0 :(得分:6)

解决此问题的一种方法是:

var entries = [{id: 1},{id: 2},{id: 3}];

for (var i = 0; i < entries.length; i++) {

    var entry = entries[i];

    document.getElementById(entry.id).onclick = (function (id) {
        return function () { 
            console.log("this.id: " + this.id);
            console.log("entry.id: " + id);
        };
    })(entry.id);

}

您可以使用自执行函数(其目的是提供entry.id将绑定的闭包)来返回绑定到特定id的新onclick处理程序。

如果你不这样做,你所有的onclick处理程序都绑定到同一个条目变量,并以for循环中收到的最后一个值结束。

答案 1 :(得分:2)

这是关闭的棘手问题之一。您正在使用entry的闭包创建这些事件处理程序。他们都可以访问该变量,即确切的变量。因此,一旦这些事件触发,它们只会获得设置为最后一个值。你需要打破关闭。这是一种方式。

function addClickHandlers() {
  var entries = [{id: 1},{id: 2},{id: 3}];
  var i = entries.length;
  while (i--) {
    var entry = entries[i];
    document.getElementById(entry.id).onclick = getClickHandler(entry);
  }
}  

function getClickHandler(entry) {
  return function() { 
    console.log("this.id: " + this.id);
    console.log("entry.id: " + entry.id);
  };
}

注意:我将for循环更改为一段时间只是因为当订单不重要时,这是在javascript中循环的最快方式,我认为它很光滑。

为完整性而添加

还有一些其他方法可以解决这种情况。我发现我一直都在使用它们。

在Ext中有createDelegate

myhandler.createDelegate(scope, [arguments], [appendArgs]);

对于javascript 1.8.5,有Function.prototype.bind

myhandler.bind(scope, arguments)

编写Function.prototype.bind非常简单(lifted from here

Function.prototype.bind = function(self, var_args) {
  var thisFunc = this;
  var leftArgs = Array.slice(arguments, 1);
  return function(var_args) {
    var args = leftArgs.concat(Array.slice(arguments, 0));
    return thisFunc.apply(self, args);
  };
};  

答案 2 :(得分:2)

这是因为闭包在JavaScript中的工作方式。所有3个元素的onclick引用存储相同的函数,该函数在循环后具有指向最后一个条目的条目。因此结果。您可以通过将其包装到具有所需条目的另一个函数中来获得结果。以下是一种方法:

var entries = [{id: 1},{id: 2},{id: 3}];

for (var i = 0; i < entries.length; i++) {

    var entry = entries[i];

    function setOnClickHandler(entry) {
        document.getElementById(entry.id).onclick = function () { 
            console.log("this.id: " + this.id);
            console.log("entry.id: " + entry.id);
        };  
    }

    setOnClickHandler(entry);
}

我在this上写了一篇小帖子。