带有循环的奇怪闭包行为

Weird closure behavior with loop

所以我已经在 SO 上学到了很多关于闭包的知识。

但我仍然发现了一个我无法向自己解释的例子:

       function buildList(list){
            var result = [];
            for(var i = 0; i < list.length; i++) {
                var item = "item: " + list[i] + " i's value: " + i;  //i has the value of 2 here, but why?
                result.push( function(){document.write(item + ", list[i]:" + list[i] + ", i's value in the anonymous function:" + i + "<br>")});
            } //i = 3 here
            return result;
        }

        function testList(){
            var testTheList = buildList([1,2,3]);
            for (var j = 0; j < testTheList.length; j++) {
                testTheList[j]();
            }
        }

        testList();

正如我对闭包的预期,当我执行 testList() 时,我应该是 3。

但结果是:

item: 3 i's value: 2, list[i]:undefined, i's value in the anonymous function:3

item: 3 i's value: 2, list[i]:undefined, i's value in the anonymous function:3

item: 3 i's value: 2, list[i]:undefined, i's value in the anonymous function:3

为什么 is i 对于 var item => 2 而 i 在匿名函数中 => 3 ?正如我所读,闭包创建了新的执行环境,但我不应该为闭包设置相同的值吗?

编辑

这不是 JavaScript closure inside loops – simple practical example 的副本,我不想知道如何创建新的执行环境。

我想知道为什么(循环的)相同变量 i 的值在相同范围内不同?

当您将新函数添加到 result 列表时,它会引用 item 变量和 i 计数器。在 buildList 函数内的循环期间,您并没有创建几个 item 变量,而是在 buildList 函数执行结束时覆盖现有变量 itemi 变量看起来像这样:

  • 3
  • "item: 3 i's value: 2"

list[i]undefined 因为你的列表长度是 3,你没有 list[3] 项目。因此,当您从 testList 中的循环调用匿名函数时,它会重新运行您与 itemi 变量保留的完全相同的值。同时在循环中创建匿名函数这不是最佳实践,我建议您像这样修改 buildList 函数:

function buildList(list) {
    var index = 0;

    return function () {
        if (index >= list.length) return
        var item = "item: " + list[index] + " i's value: " + index;
        document.body.innerHTML += [
          item, ", list[i]:", list[index], 
          ", i's value in the anonymous function:",
          index, "<br>"
        ].join('')
        index++
    }
}

function testList() {
    var list = [1, 2, 3];
    var testTheList = buildList(list);
    document.body.innerHTML = '';
    for (var j = 0; j < list.length; j++) {
        testTheList();
    }
}

testList();