从Javascript关闭循环访问外部变量

IT技术 javascript jquery loops closures
2021-02-06 00:49:05

看:

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 始终是数组中最后一个项目的 id (this.items)。怎么解决?

5个回答

您在这里遇到的问题是变量item随着每个循环而变化。当您item稍后引用时,将使用它保存的最后一个值。您可以使用称为闭包的技术(本质上是一个返回函数的函数)来快速地以不同的方式作用域变量。

    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);
  });
});
谢谢!我只是花了一些时间尝试创建一个闭包以在我的点击回调中使用局部变量。对我来说,从返回的函数中删除参数的棘手部分。
2021-03-16 00:49:05
事后为您找到了更优雅的解决方案 - 查看使用的优势 $.each()
2021-03-24 00:49:05

解决此问题的另一种方法是确保= 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]);
}

那里的匿名函数有点混乱,因此最好使用一个不那么匿名的函数来达到目的,但它确实有效。

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都会创建一个单独的闭包,该闭包有自己的变量。

试试这个循环

for (var i=0; i < this.items.length; i++) {
  this.items[i]
};
不,循环没问题。问题是每个添加的列表项在闭包中看到相同的“item.id”。
2021-03-31 00:49:05

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

在新的 1.4 版本的库中,他们添加了 jQuery.proxy() 函数这使您能够有效地修改您正在调用的函数的上下文/范围 - 以 jQuery 方式完成,这确保您可以停止使用可能会造成混乱的技术。