2

我使用这行代码将事件侦听器添加到通过 forloop 创建的 div 中:

for(var i in mail){
   //create div
   parent.addEventListener("click",function(){read_msg(mail[i].id);},false);
   //append to parent
}

这导致了mail[i].id成为所有人的最后一个 id 的问题。我已经阅读了一些有关如何解决它的示例,但我发现它仍然很混乱。

我被建议解决方案:

(function(){read_msg(mail[this].id)}).bind(i);

但是有人告诉我这不是一个很好的解决方案,希望有人能解释你如何read_msg保持正确的价值id?就解决方案而言,它似乎总是有点混乱。

4

3 回答 3

1

这是因为您i在事件处理函数中使用了闭包变量。

变量i驻留在外部函数的范围内,即变量只有一个实例i。当调用和i访问处理程序方法时,javascript会首先查看处理程序函数的作用域,如果在那里找不到变量,它将查看父闭包作用域,然后它会在父作用域中找到变量。

在父作用域中,随着循环的执行,值不断变化,i这就是为什么所有回调都具有相同的值的原因i

这里的解决方案是创建一个本地闭包

for(var i in mail){
    (function(myvar){
        parent.addEventListener("click",function(){read_msg(mail[myvar].id);},false);
        //append to parent
    })(i);
}

这里我们要做的是我们有一个立即调用的函数表达式,我们将i作为参数的值传递给它myvar。所以循环中的每次迭代都会创建一个独立的闭包。

于 2013-06-13T03:17:24.193 回答
1

您可以在大多数浏览器中使用 Object+Array 方法来避开 js 中讨厌的循环范围“错误”:

 function addClick(key){
   //create div
   parent.addEventListener("click",function(){read_msg(mail[key].id);},false);
   //append to parent
}   

Object.keys(mail).forEach(addClick);

因为函数有作用域并且 forEach 吃函数,所以在使用 Array 方法时不需要额外的匿名包装器。

如果你想全力以赴新的 JS 热度:

 function addClick(key){

   parent.addEventListener("click", this.method.bind( this.elm, this.source[key].id ), false);

}   

Object.keys(mail).forEach(addClick, {elm:parent, source: mail, method:read_msg });

您反转键的源对象以允许使用“邮件”以外的对象、“父”以外的元素附加事件以及“read_msg”以外的方法,所有这些都无需触及逻辑或使用“ return"... 基本上,无论您在传统 C 风格的 for 循环初始化程序中设置什么,都可以移至 this.something 并在需要时重新应用逻辑。

于 2013-06-13T03:32:47.273 回答
0

因为您定义为每个循环迭代的事件处理程序的每个匿名函数都将与所有其他函数共享相同的范围,它们都将引用相同的 var (i) 作为您尝试显示的消息的数组地址。因为您正在使用每个循环重新定义 var i,所以您将始终看到消息数组中的最后一条消息显示在每个点击事件上,因为分配给 i 的最后一个值将是您的“邮件”数组的长度。

继承人如何解决它:

var helper = function(index) {
    parent.addEventListener("click", function(){read_msg(mail[index].id);},false);
}

for(var i in mail) {
    helper(i);
}
于 2013-06-13T03:55:42.777 回答