在循环中分配方法时出现非常奇怪的行为

3
考虑下面的代码:

考虑下面的代码:

<a href="javascript:void(-1)" id="a1">a1</a>
<a href="javascript:void(-1)" id="a2">a2</a>

<script type="text/javascript">
    var buttons = []
    buttons.push("a1")
    buttons.push("a2")
    var actions = []
    for (var i in buttons)
    {
        actions[buttons[i]] = function() { alert(i) }
    }

    var elements = document.getElementsByTagName("a")
    for (var k = 0; k < elements.length; k++)
    {
        elements[k].onclick = actions[elements[k].id]
    }
    
</script>

基本上,它显示了两个锚点a1和a2,我希望在单击相应的锚点时弹出“1”和“2”的警报。 但是并没有发生这种情况,无论单击哪个都会得到“2”。在花费一个小时沉思代码后,我认为这可能是因为动态onclick方法对于两个锚点都保留了“i”的最后一个值。 所以我将那个循环改成了:

for (var i in buttons)
{
    var local_i = i.toString()
    actions[buttons[i]] = function() { alert(local_i) }
}

希望每个动态函数都能获得自己的"i"的副本,其值为即时值。但是在这个更改后,当我单击任何一个链接时,会出现"1"。
我做错了什么?这对我来说是一个巨大的障碍。

1
我已经回答过的可能重复问题链接:https://dev59.com/SHI_5IYBdhLWcg3wDOdC http://stackoverflow.com/questions/1579978/ - Christian C. Salvadó
和你在那两篇帖子中回答的问题完全一样。+1 - Andrey
我希望你注意到了我的最后一次编辑。 - Gumbo
3个回答

3
最后一个值被存储了,你可以使用闭包来实现这个功能:
<a  href="#">blah</a><br>
<a  href="#">blah</a><br>
<a  href="#">foo</a><br>

<script>
    (function() {
    var anchors = document.getElementsByTagName('a');
    for ( var i = anchors.length; i--; ) {
        var link = anchors[i];
        (function(i) {
            link.onclick = function() {
                alert(i)
            }
        })(i);
    }
    })();
</script>

这个解决方案将i绑定到函数作用域中,关键是在循环内执行函数,否则你最终得到的是迭代并警报i的最后一个值。
参考资料:http://www.jibbering.com/faq/faq_notes/closures.html

但我不明白,为什么它没有将local_i绑定到函数作用域中,就像我在第二个示例中尝试的那样? - Andrey
1
为了使其正常工作,您需要在循环内部执行一个函数。 - meder omuraliev
1
@Andrey - 因为Javascript没有块级作用域,只有函数级作用域。当你在一个函数中声明一个变量时,使用var,它的作用域限定于该函数,即使它位于一个for循环内部。通过上面meder所做的操作,你创建了一个新的作用域。 - Matt

2
这篇博客文章很好地解释了这个问题。问题在于JavaScript中的循环没有自己的变量作用域,因此内部函数使用父函数的作用域。

-1

不要在数组中使用for (… in …),而应该使用简单的for循环:

for (var i=0; i<buttons.length; ++i) {
    actions[buttons[i]] = (function(i) {
        return function() {
            alert(i);
        };
    })(i);
}

但是重点仍然存在。+1。不过这也可以是一条评论。 - Chetan S
这是一个怎样的 +1 呢? "for (var i = 0 ...)"如何比 "for (var i in ...)"更好? 对我来说,这只是语法糖 - 我错了吗? - Andrey
3
@Andrey: for...in语句的用途是迭代对象属性,这个语句存在的问题是它会沿着原型链向上查找,如果Array.prototype已经被扩展了,那么这些扩展也会被迭代(如果你不使用hasOwnProperty),而且这个语句也不能确保迭代的顺序。 - Christian C. Salvadó
我理解你的观点。不幸的是,它不允许我取消我的反对票 - "投票时间太久了"。 - Andrey

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