在for循环中创建闭包 - 我这样做了吗?

时间:2012-02-22 17:50:03

标签: javascript jquery

我很怀疑。在这里相当密集,但我无法弄清楚下面的代码到底发生了什么。

我要做的是将两个独立的处理程序附加到字段的change事件中。每个处理程序都是通过循环数组并使用数组中的项来设置的,以便在运行时实现处理程序的输出 - 希望在查看示例代码时变得清晰。

代码如下:

    $(document).ready( function () {
        //
        // Create some test input fields on the page...
        //
        $('<br />').insertAfter($('body > *:last'));
        $('<input type="text" name="t0" id="t0" value="" />').insertAfter($('body > *:last'));
        $('<input type="text" name="t1" id="t1" value="" />').insertAfter($('body > *:last'));

        //
        // The problematic part  - for me at least...
        //
        var arr = new Array(1, 2);
        for (var a in arr) {
            // Using Chrome console here for logging
            console.log("## " + a);
            $('#t0').change(function () {
                console.log(">> " + a)
            });
        }
    });

因此,当我在第一个字段中添加值时,我期望发生的事情是从控制台内部(在Chrome中运行这些示例):

## 0
## 1
>> 1
>> 2

我得到的是:

## 0
## 1
>> 1
>> 1

我原本希望传递给处理程序的函数会在a的值上形成一个闭包,我最终将两个函数绑定到处理程序,其中a具有值1以及a具有值2

的值

想法?

干杯 - 克里斯

3 个答案:

答案 0 :(得分:4)

这里有两个重大错误:

首先,for (a in x)不能像你期望的那样工作:它迭代对象属性,而不是数组元素。

另一个错误是a在调用函数时发生了变化。实现所需功能的好方法是:

for(var a=0; a<arr.length; a++) {
    (function(a) {
        // now you can use "a"
    })(arr[a]);
}

如果你没有创建一个闭包,看看for循环会发生什么,请看:

var arr = [1,2,3];
var functions = [];

for(var a=0; a<arr.length; a++) {
    functions.push(function() {
        console.log(a);
    })
}

// now execute all the functions
for(var i=0; i<functions.length; i++) {
    functions[i]();
}

现在所有函数都会记录3,这是数组中最后一个元素的索引+ 1(arr[0] == 1, arr[1] == 2, arr[2] == 3)。会发生的是for循环在每次迭代时创建这些函数,但是在循环结束后,a == arr.length时会执行

答案 1 :(得分:2)

尝试jQuery的own tool

,而不是“for”循环
 $(arr).each(function(a) {
       console.log("## " + a);
        $('#t0').change(function () {
            console.log(">> " + a)
        });
  });

这可以按预期工作。

您的代码不起作用的原因是闭包使用其变量的最新值。也就是说,如果你创建一个a等于1的闭包,然后当a等于2时创建另一个闭包,那么两个闭包都将使用最后一个值,2。这无疑是令人困惑的,但这是这个怎么运作。

答案 2 :(得分:-1)

你必须做这样的事情:

for (var a in arr) {
    (function (a) {
        // thanks to closure variable a is local here
        $('#t0').change(function () {
            console.log(">> " + a);
        });
    }(a)); 
}

或者像这样:

for (var a in arr) {
    $('#t0').change((function (a) {
        return function () {
            // returned function has access to local variable a from 
            // outer function
            console.log(">> " + a);
        };
    }(a));
}

Trick是将变量a放在比循环更深的范围内。 为了更容易阅读上面的两个片段,可以写成:

for (var a in arr) {
    (function (inner_a) {
        // thanks to closure variable a is local here
        $('#t0').change(function () {
            console.log(">> " + inner_a);
        });
    }(a)); 
}

for (var a in arr) {
    $('#t0').change((function (inner_a) {
        return function () {
            // returned function has access to local variable a from 
            // outer function
            console.log(">> " + inner_a);
        };
    }(a));
}

顺便说一句。除非你通过对象属性进行交互,否则最好使用classic for(var i ...)而不是for .. in循环。并且$('#t0')应该被缓存:

var cachedEl = $('#t0');

for (var a=0; a < arr.length; a++) {
    (function (inner_a) {
        // thanks to closure variable a is local here
        cachedEl.change(function () {
            console.log(">> " + inner_a);
        });
    }(a)); 
}
// or
for (var a=0; a < arr.length; a++) {
    cachedEl.change((function (inner_a) {
        return function () {
            // returned function has access to local variable a from 
            // outer function
            console.log(">> " + inner_a);
        };
    }(a));
}