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

5

我正在尝试在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);
    };

}

What I expect is:

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

为什么entry对象总是具有id 3的条目?

如何在click事件处理程序内获取正确的entry对象?


1
您已经通过event.target.idevent.currentTarget.id属性正确地获取了值。控制台接收到输入值3是因为它是for循环中的最后一次迭代。 - user1385191
3个回答

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绑定到其中),以返回一个新的onclick处理程序,该处理程序绑定到特定的id。
如果您不这样做,所有的onclick处理程序都会绑定到相同的entry变量,并最终以for循环期间它接收到的最后一个值结束。

2

这是闭包中的一个棘手问题。你使用闭包来创建那些事件处理程序,它们都对entry进行了闭包。它们都可以访问那个变量,确切地说,是那个变量。因此,一旦这些事件触发,它们只会得到最后设置为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循环改为while循环,只是因为在JavaScript中,当顺序不重要时,这是最快的循环方式,我认为它很棒。
为了完整起见添加:
还有一些其他方法专门用于处理这种情况。我发现我经常使用它们。
在Ext中,有一个createDelegate方法。
myhandler.createDelegate(scope, [arguments], [appendArgs]);

对于JavaScript 1.8.5,有Function.prototype.bind函数。
myhandler.bind(scope, arguments)

编写 Function.prototype.bind 非常容易 (此处引用)

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

这是因为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);
}

我已经在这里写了一篇关于闭包的小文章。


1
这里提出的所有解决方案都很好,但我更喜欢您的解决方案,因为我觉得它更清晰易懂。 - Yaz

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