Javascript - 关闭概念不清楚

时间:2015-01-05 18:21:11

标签: javascript closures

请参考JSfiddle - http://jsfiddle.net/r0ejq8h8/

var buttons = document.getElementsByTagName("button");

for (i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", function () {
    alert("You just clicked " + i);
});
}

它总是警告“你刚刚点击了5”。我理解这一点。这个问题可能有很多解决方案。我找到的其中一个就是JSfiddle。在网上没有任何解释是足够好的。有人可以详细向我解释“i”发生了什么。任何其他有解释的解决方案也会很棒。

3 个答案:

答案 0 :(得分:1)

最简单的方法是根据时间表来考虑这一点。

i是一个指向值的变量名。当你经历循环(第一件事)时,我会不断重置为更高的值(先是0,然后是1等)。每次迭代时,都会将函数绑定到按钮。该函数尚未调用,它引用i

稍后,当单击一个按钮时,该函数将执行并查找i的值。由于此时循环已完成,i将等于按钮数(在本例中为5)。

时间表

基本上是这样的:

  • i设为0

  • 第一个函数被绑定

  • i设为1

  • 第二个函数已绑定

...

  • i设置为n(按钮数量)

  • i大于循环条件且循环完成

  • 用户点击按钮

  • 触发了回调并且引用i当前设置为n

  • 警告会以n为值进行触发。

解决问题

有很多方法可以解决这个问题。最简单的旧(向后兼容方式)是将当前值传递给函数(因此立即读取),然后使用它返回一个新函数,该函数将旧值保存在闭包中:

for (i = 0; i < buttons.length; i++) {
  buttons[i].addEventListener("click", createHandler(i));
}

function createHandler(val) {
  return function() {
    alert("You just clicked " + val);
  }
} 

在所有浏览器中可能不起作用的新(ES6)方法是使用let,它限制变量以阻止范围。

for (let i = 0; i < buttons.length; i++) {
  buttons[i].addEventListener("click", function () {
    alert("You just clicked " + i);
  });
}

答案 1 :(得分:0)

单击该按钮时,会在单击该按钮时提醒i的值。

这将在循环结束并且i处于其最高值之后。

这是因为i由您传递给addEventListener的匿名函数关闭。


通过使用其他功能,您可以在调用该功能时复制i的值。

var buttons = document.getElementsByTagName("button");

for (i = 0; i < buttons.length; i++) {
    add_event_handler(buttons[i], i);
}

function add_event_handler(element, value_to_alert) {
    element.addEventListener("click", function () {
        alert("You just clicked " + value_to_alert);
    });
  
}
<button>X</button>
<button>X</button>
<button>X</button>
<button>X</button>

答案 2 :(得分:0)

举个例子,

var buttons = document.getElementsByTagName("button");

for (i = 0; i < buttons.length; i++) {
  buttons[i].addEventListener("click", function () {
    alert("You just clicked " + i);
  });
}

您为click button事件注册的匿名函数包含值i。所有人都共享相同的变量i,并且将具有最近的值,该值已在for循环结束时更新。这就是为什么click事件总是警告You just clicked 5


修复

var buttons = document.getElementsByTagName("button");

function createAlert(i) {
    return function() {
        alert("You just clicked " + i);
    }
}

for (i = 0; i < buttons.length; i++) {
    buttons[i].addEventListener("click",createAlert(i));
}

在此修复程序中,alert函数现在取决于createAlert函数创建的环境。因此,对于每次迭代,返回的function中的每一个都将包含由i循环的迭代给出的for的特定值


更新小提琴

http://jsfiddle.net/r0ejq8h8/