在for循环中创建闭包——我这样做对吗

Creating closures within a for loop - am I doing this right?

本文关键字:这样做 闭包 for 循环 创建      更新时间:2023-09-26

我有问题。这里非常密集,但我无法弄清楚下面代码中到底发生了什么。

我要做的是将两个独立的处理程序附加到字段的更改事件。每个处理程序都是通过在数组上循环设置的,并在处理程序运行时使用数组中的项来影响处理程序的输出——希望在查看示例代码时会变得清楚。

代码如下:

    $(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

想法?

Cheers-kris

这里有两个大错误:

首先,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时,它们会被执行

不要使用"for"循环,而是使用jQuery自己的工具。

 $(arr).each(function(a) {
       console.log("## " + a);
        $('#t0').change(function () {
            console.log(">> " + a)
        });
  });

这符合预期。

代码不起作用的原因是闭包使用了其变量的最新值。也就是说,如果创建一个a等于1的闭包,然后在a等于2时创建另一个闭包,那么两个闭包都将使用最后一个值2。这确实令人困惑,但这就是它的工作原理。

你必须这样做:

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));
}

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

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。。循环中,除非您正在交互对象属性。并且$('#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));
}