我正在构建一个简单的计算器应用程序。我正在尝试完成三件事:


将事件侦听器分配给按钮。
单击该按钮时,触发一个事件。
使用eventListener函数显示单击按钮的值。

for (i = 0; i < btn.length; i++) {
    var btnVal = btn[i].value;
    btn[i].addEventListener("click", function() { displayNumber(btnVal) }, false);
}

function displayNumber(param) {
    displayedNum.innerHTML = param;
}



传递给事件侦听器函数时,似乎无法访问btnVal。

最佳答案

在循环内定义事件监听器(或其他异步事物)非常棘手。您可能会认为您正在创建几个不同的btnVal变量,每次在循环中都创建一个变量,但事实并非如此。该var btnVal被吊起并重新使用,因此您的代码最终表现如下:

var btnVal;
for (i = 0; i < btn.length; i++) {
   btnVal = btn[i].value;
   btn[i].addEventListener("click", function() { displayNumber(btnVal) }, false);
}


因此,所有事件侦听器都与同一个变量进行交互,并且当最终单击它们时,他们将仅看到分配给btnVal的最后一个值,该值应为btn[btn.length -1].value。数组前面的所有值都将丢失。

有几种方法可以解决此问题:

1)您可以在事件结束时将其从元素本身中拉出,而不是依赖于闭包变量。

for (i = 0; i < btn.length; i++) {
  btn[i].addEventListener("click", function (event) {
    displayNumber(event.target.value);
  });
}


2)将事件侦听器的创建移到一个函数中,并传入btnVal。由于它现在是一个函数参数,因此它将获得一个新的绑定。

for (i = 0; i < btn.length; i++) {
    createListener(btn[i], btn[i].value);
}

function createListener(element, val) {
    element.addEventListener("click", function () { displayNumber(val) }, false);
}


3)您可以使用IIFE内联。

for (i = 0; i < btn.length; i++) {
   (function (button) {
       button.addEventListener("click", function () { displayNumber(button.value) }, false);
   })(btn[i]);
}


编辑:添加选项4

4)如果可以使用ES2015,请使用let。 Let具有块作用域,并且每次通过循环都会获得一个新的绑定

for (i = 0; i < btn.length; i++) {
   let btnVal = btn[i].value;
   btn[i].addEventListener("click", function() { displayNumber(btnVal) }, false);
}

10-07 21:26