JavaScript回调函数中的变量总是在循环中获得最后一个值?

10 javascript addeventlistener

我正在尝试执行以下操作:

我有一组图像和选择(下拉)HTML元素,每个元素30个.我正在尝试在1到30的循环上使用AddEventListener,这样当我更改select的值时,图像src会更新(并且图像会更改).

AddEventListener函数就是这个:

function AddEventListener(element, eventType, handler, capture)
{
    if (element.addEventListener)
        element.addEventListener(eventType, handler, capture);
    else if (element.attachEvent)
        element.attachEvent("on" + eventType, handler);
}
Run Code Online (Sandbox Code Playgroud)

我试过这个并且它有效:

var urlfolderanimalimages = "http://localhost/animalimages/";
var testselect = "sel15";
var testimg = "i15";

AddEventListener(document.getElementById(testselect), "change", function(e) {
    document.getElementById(testimg).src = urlfolderanimalimages + document.getElementById(testselect).value;
    document.getElementById(testimg).style.display = 'inline';

    if (e.preventDefault) e.preventDefault();
    else e.returnResult = false;
    if (e.stopPropagation) e.stopPropagation();
    else e.cancelBubble = true;
}, false);
Run Code Online (Sandbox Code Playgroud)

但后来我尝试在循环中调用它并且它不起作用.该事件已添加,但当我更改任何选择时,它将更新最后一个(ID为i30的图像).

var urlfolderanimalimages = "http://localhost/animalimages/";

for (k=1;k<=30;k++) {
    var idselect = "sel" + k;
    var idimage = "i" + k;

    AddEventListener(document.getElementById(idselect), "change", function(e) {
        document.getElementById(idimage).src = urlfolderanimalimages + document.getElementById(idselect).value;
        document.getElementById(idimage).style.display = 'inline';

        if (e.preventDefault) e.preventDefault();
        else e.returnResult = false;
        if (e.stopPropagation) e.stopPropagation();
        else e.cancelBubble = true;
    }, false);

}
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?我是JavaScript(和一般编程)的新手,很抱歉呕吐诱导代码:(

小智 8

问题是你正在"关闭" 同一个变量.

var不声明变量1.它只是为给定执行上下文中的标识符注释"停止查找".

有关所有详细信息,请参阅Javascript Closures"FAQ Notes"."执行上下文"和"范围链"部分最有趣.

常见的习惯用法是执行双重绑定以创建新的执行上下文.

例如

var k
for (k = 1; k < 10; k++) {
  setTimeout((function (_k) {
    return function () {
      alert(_k)
    }
  })(k), k * 100)
}
Run Code Online (Sandbox Code Playgroud)

从JavaScript第5版开始Function.bind(它也可以在第3版中实现,例如来自prototype.js的bind),可以这样使用:

var k
for (k = 1; k < 10; k++) {
  setTimeout((function () {
     alert(this)
  }).bind(k), k * 100)
}
Run Code Online (Sandbox Code Playgroud)

1该构建称为在ECMAScript规范一个变量声明.然而,这一陈述虽然可能被视为具有误导性,但强调要提出一个观点:另见"变量提升".