【问题标题】:Access outside variable in loop from Javascript closure [duplicate]从Javascript闭包访问循环中的外部变量[重复]
【发布时间】:2010-11-22 20:07:10
【问题描述】:

见:

for (var i in this.items) {
    var item = this.items[i];
    $("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>");
    $("#showcasebutton_"+item.id).click(function() {
        alert(item.id);
        self.switchto(item.id);
    });
}

问题是警报 item.id 始终是数组 (this.items) 中最后一项的 id。如何解决?

【问题讨论】:

    标签: javascript jquery loops closures


    【解决方案1】:

    试试这个循环

    for (var i=0; i < this.items.length; i++) {
      this.items[i]
    };
    

    【讨论】:

    • 不,循环没问题。问题是每个添加的列表项都会在闭包中看到相同的“item.id”。
    【解决方案2】:

    这里的问题是变量item 随每个循环而变化。当您稍后引用item 时,将使用它保存的最后一个值。您可以使用一种称为closure(本质上是一个返回函数的函数)的技术来以不同的方式快速确定变量的范围。

        for (var i in this.items) {
                var item = this.items[i];
                $("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>");
                $("#showcasebutton_"+item.id).click( 
                    // create an anonymous function that will scope "item"
                    (function(item) {
                       // that returns our function 
                       return function() {
                        alert(item.id);
                        self.switchto(item.id);
                       };
                    })(item) // immediately call it with "item"
                );
        }
    

    附注 - 我看到你在这里有 jQuery。它有一个辅助函数$.each(),可以与数组一起使用,并且可以作为简单for/each循环的快捷方式。由于此调用中作用域的工作方式 - 您不需要使用闭包,因为“item”在调用时已经是函数的参数,而不是存储在父函数范围内的 var 中,就像是真的一样在你的例子中。

    $.each(this.items,function(i, item) {
      $("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>");
      $("#showcasebutton_"+item.id).click(function() {
        alert(item.id);
        self.switchto(item.id);
      });
    });
    

    【讨论】:

    • 事后为您找到了更优雅的解决方案 - 查看使用 $.each() 的优势
    • 谢谢!我只是花了一些时间尝试创建一个闭包以在我的点击回调中使用局部变量。对我来说,从返回的函数中删除参数的棘手部分。
    【解决方案3】:

    解决此问题的另一种方法是确保通过调用函数有效地完成= items[i] 业务。简而言之,这是:

    for (var i in this.items) {
        (function(item) {
            $("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>");
            $("#showcasebutton_"+item.id).click(function() {
                alert(item.id);
                self.switchto(item.id);
            });
        })(this.items[i]);
    }
    

    那里的匿名函数有点乱,因此最好有一个不那么匿名的函数,但它可以解决问题。

    【讨论】:

      【解决方案4】:

      Javascript 闭包存储对其变量的引用,因此您的所有 onclick 处理程序都使用相同的变量。

      您需要在中间函数中捕获变量,如下所示:

      function buildClickHandler(pageNumber) {
          return function()  {    //Create and return a new function
              alert(item.id);
              self.switchto(item.id);
          }
      }
      

      然后,使用该函数创建click 处理程序,如下所示:

      for (var i in this.items) {
          var item = this.items[i];
          $("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>");
      
          $("#showcasebutton_"+item.id).click(buildClickHandler(item));
      }
      

      buildClickHandler 的每次调用都会创建一个具有自己变量的单独闭包。

      【讨论】:

        【解决方案5】:

        我很清楚这是一篇旧帖子,但据我所知,设计 jQuery(我认为您一定在使用)的天才们似乎已经为您的问题提供了最佳解决方案。

        在库的新 1.4 版本中,they have added the jQuery.proxy() function。这使您能够有效地修改您正在调用的函数的上下文/范围 - 以 jQuery 方式完成,这确保您可以停止使用可能会搞砸事情的技术。

        【讨论】:

          猜你喜欢
          • 2013-09-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-03-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多