1

可能重复:
循环内的 Javascript 闭包 - 简单的实际示例

我有一个包含 4 个对象 () 的数组that.pairs,每个对象都有.t一个 jQuery 对象/元素的属性。我正在尝试在每个t被点击时设置一个事件。

问题是,当其中一个被点击时,总是最后一对(索引 3)被传递到我的doToggle()函数中。

为什么会这样?我该如何解决?

for (var i = 0; i < that.pairs.length; i++) {
    var p = that.pairs[i];
    p.t.click(function() {
        that.doToggle(p);
    });
}
4

4 回答 4

4

这是因为该p变量由您的闭包共享,因此只有一个 p 变量。当你的处理程序被调用时,p 已经改变了。

你必须使用我称之为冻结你的闭包的技术

for (var i = 0; i < that.pairs.length; i++) {
    // The extra function call creates a separate closure for each
    // iteration of the loop
    (function(p){
        p.t.click(function() {
            that.doToggle(p);
        });
    })(that.pairs[i]); //passing the variable to freeze, creating a new closure
}

实现此目的的一种更易于理解的方法如下

function createHandler(that, p) {
    return function() {
       that.doToggle(p);
    }
}

for (var i = 0; i < that.pairs.length; i++) {
    var p = that.pairs[i];
    // Because we're calling a function that returns the handler
    // a new closure is created that keeps the current value of that and p
    p.t.click(createHandler(that, p));
}

闭包优化

由于在评论中有很多关于闭包的讨论,我决定放这两个屏幕截图,显示闭包得到了优化,并且只包含了所需的变量

此示例http://jsfiddle.net/TnGxJ/2/显示了如何仅a包含 没有评估的闭包

在这个例子中http://jsfiddle.net/TnGxJ/1/,因为有一个eval,所以所有的变量都被括起来了。 用 eval 闭包

于 2012-11-29T00:03:58.413 回答
3

使用$.each而不是for循环,以便每次迭代都获得一个新的变量范围。

$.each(that.pairs, function(i, p) {
    p.t.click(function() {
        that.doToggle(p);
    });
});

这样,每个click处理程序都会关闭一个唯一的变量范围,而不是共享的外部变量范围。

于 2012-11-29T00:04:34.567 回答
1
for (var i = 0; i < that.pairs.length; i++) {
    var p = that.pairs[i];
    (function(p){
        p.t.click(function() {
            that.doToggle(p);
        });
    }(p));
}

IIFE 的这个技巧将解决您现在遇到的关闭“问题”。

于 2012-11-29T00:03:47.450 回答
1
for (var i = 0; i < that.pairs.length; i++) {
    (function(num){
       var p = that.pairs[num];
       p.t.click(function() {
          that.doToggle(p);
       });
    })(i)
}

经典关闭问题

将它们包含在一个匿名函数中,并在上下文中分配当前迭代。那应该可以解决问题..

于 2012-11-29T00:03:55.197 回答