可能重复:
Javascript closure inside loops - simple practical example
所以我有一个包含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);
});
}
答案 0 :(得分: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
,所有变量都包含在内。
答案 1 :(得分:3)
使用$.each
代替for
循环,以便每次迭代都获得一个新的变量范围。
$.each(that.pairs, function(i, p) {
p.t.click(function() {
that.doToggle(p);
});
});
这样,每个click
处理程序都会关闭一个唯一的变量作用域而不是共享的外部变量作用域。
答案 2 :(得分: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的这个技巧将解决你现在正在经历的关闭“问题”。
答案 3 :(得分: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)
}
经典关闭问题
将它们包含在匿名函数中,并在上下文中分配当前迭代。那应该可以解决问题..