关于javascript:关于创建动态按钮的循环,但无法给出正确的值

About a loop that creates dynamic buttons, but cannot give proper values

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Javascript infamous Loop problem?

我有一个小问题,如果你们中的一些人能够意识到这里缺少什么样的逻辑,那将是非常好的,因为我似乎无法找到它:

我有一个数组,其中包含一些先前操作的结果。假设数组是:

1
var results = [0, 1];

然后我有一堆代码,我创建了一些按钮,在for循环中,我为这些按钮分配了不同的功能,具体取决于数组的位置。问题在于,由于某种原因,所有创建的按钮(在这种情况下为两个)都会出现分配给数组最后一个值的函数(在这种情况下,两个都将作为一个,而不是第一个为0和第二个与1)

这是代码:

1
2
3
4
5
6
7
8
9
10
11
12
for (var i = 0; i < results.length; i++) {
    var br2 = b.document.createElement("br");
    var reslabel = b.document.createTextNode(Nom[results[i]].toString());
    var card = document.createElement("input");
    card.type ="button";
    id = results[i]; // this is the problematic value.
    card.onclick = newcard; // this function will use the above value.
    card.value ="Show card";
    divcontainer.appendChild(br2);
    divcontainer.appendChild(reslabel);
    divcontainer.appendChild(card);
}

实际上,这个代码产生的数量与数组中的元素一样多,每个按钮都有适当的标签(它从另一个数组中检索标签)。一切都很好。然后,我点击按钮。所有按钮都应该运行newcard功能。该函数需要id变量,因此在这种情况下它应该是:

  • 第一个按钮:使用值为0的变量id运行newcard
  • 第二个按钮:使用值为1的变量id运行newcard

但是两个按钮都使用id作为1运行......为什么会这样?

它可能很简单,或者可能只是在我的时区已经很晚了:-)无论如何,我将不胜感激任何评论。我在这里学到很多东西......

谢谢!

编辑以添加newcard的定义:

1
2
3
4
5
6
function newcard() {
    id = id;
    var toerase = window.document.getElementById("oldcard");
    toerase.innerHTML ="";
    generate();
}

函数generate将使用id生成一些内容。它没有错,它生成的内容很好,只是id始终设置为数组中的最后一项。


id是一个全局变量,当循环结束时,它被设置为数组的最后一个值。当事件处理程序代码运行并询问id的值时,它将获得最后一个值。

你需要创建一个闭包来捕获当前的results[i]并传递它(这是一个非常常见的pitfal,请参阅Javascript臭名昭着的循环问题?)。由于newcard非常简单,并且id实际上在generate中使用,因此您可以修改generate以将id作为参数。那么你不再需要newcard了,你可以这样做:

1
2
3
4
5
6
card.onclick = (function(id) {
    return function() {
        window.document.getElementById("oldcard").innerHTML ="";
        generate(id);
    };
}(results[i]));

这样做是定义并立即调用传递当前results[i]的函数。它返回另一个函数,它将是您的实际onclick处理程序。该函数可以访问外部函数的id参数(称为闭包)。在循环的每次迭代中,将创建一个新的闭包,捕获每个单独的id以供自己使用。


在继续之前,非常感谢bfavaretto解释了一些完全逃脱我的范围。看来,除了你遇到的问题之外,你还遭受了范围界定,当我试图找到答案时,这让我感到痛苦。

无论如何,这是一个有效的例子。我正在使用forEach,某些浏览器可能不支持。然而它确实解决了一些给你悲伤的范围肮脏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<body>

var results = [0,1];
results.forEach( function(result) {
    var card = document.createElement("input");
    card.type ="button";
    card.onclick = function() {
        newcard( result );
    }
    card.value ="Show card";
    document.body.appendChild(card);
});

function newcard(x) {
   alert(x);
}

</body>
</html>

如果您决定坚持使用传统循环,请参阅bfavaretto的回答。