JavaScript 不支持带有局部变量的闭包吗?

IT技术 javascript loops closures
2021-02-08 10:03:20

我对这段代码感到非常困惑:

var closures = [];
function create() {
  for (var i = 0; i < 5; i++) {
    closures[i] = function() {
      alert("i = " + i);
    };
  }
}

function run() {
  for (var i = 0; i < 5; i++) {
    closures[i]();
  }
}

create();
run();

根据我的理解,它应该打印0,1,2,3,4(这不是闭包的概念吗?)。

相反,它打印5,5,5,5,5.

我试过犀牛和火狐。有人可以向我解释这种行为吗?

6个回答

通过添加额外的匿名函数修复了 Jon 的答案:

function create() {
  for (var i = 0; i < 5; i++) {
    closures[i] = (function(tmp) {
        return function() {
          alert("i = " + tmp);
        };
    })(i);
  }
}

解释是 JavaScript 的作用域是函数级的,而不是块级的,创建闭包只是意味着将封闭作用域添加到封闭函数的词法环境中。

循环终止后,函数级变量i的值为5,这就是内部函数“看到”的。


附带说明:您应该注意不必要的函数对象创建,尤其是在循环中;它效率低下,如果涉及 DOM 对象,很容易创建循环引用,从而在 Internet Explorer 中引入内存泄漏。

我想这可能是你想要的:

var closures = [];

function createClosure(i) {
    closures[i] = function() {
        alert("i = " + i);
    };
}

function create() {
    for (var i = 0; i < 5; i++) {
        createClosure(i);
    }
}

解决方案是让一个自动执行的 lambda 包装你的数组推送。您还将 i 作为参数传递给该 lambda。自执行 lambda 中 i 的值将影响原始 i 的值,一切都会按预期工作:

function create() {
    for (var i = 0; i < 5; i++) (function(i) {
        closures[i] = function() {
            alert("i = " + i);
        };
    })(i);
}

另一种解决方案是创建另一个闭包,它捕获 i 的正确值并将其分配给另一个变量,该变量将在最终 lambda 中“被捕获”:

function create() {
    for (var i = 0; i < 5; i++) (function() {
        var x = i;

        closures.push(function() {
            alert("i = " + x);
        });
    })();
}
为了使第一个实现更清晰易懂,您可以对内部函数使用与 i 不同的参数名称!
2021-04-05 10:03:20
@Chetan Sastry,我并不是在那之后。如您所见,即使是自执行 lambda 的位置也很奇怪。好像一开始就没有问题。
2021-04-11 10:03:20

是的,关闭在这里工作。每次循环创建的函数时,都会抓取i. 您创建的每个函数都共享相同的i. 您看到的问题是,由于它们都共享相同i的值,i因此它们也共享 的最终值,因为它是相同的捕获变量。

编辑: Skeet 先生的这篇文章在一定程度上解释了闭包,并特别以比我在这里提供的信息更多的方式解决了这个问题。 但是要小心,因为 Javascript 和 C# 处理闭包的方式有一些细微的差异。 跳至“比较捕获策略:复杂性与功率”一节,了解他对这个问题的解释。

你说得很好——我想传达的主要内容是多个闭包如何共享一个变量。
2021-03-18 10:03:20
那么他的解决方法是什么(我现在也很好奇)?
2021-03-20 10:03:20
@Andrew:这可能是值得在你的答案明确地说,JavaScript的拍摄变量相同的方式工作-这就是为什么我试图修复没有工作。
2021-03-25 10:03:20
这是一篇不错的文章,但似乎在 C# 和 Javascript 之间如何实现闭包方面存在一些差异。这使得这篇文章对 OP 的问题没有帮助。
2021-03-27 10:03:20

John Resig 的Learning Advanced JavaScript解释了这一点以及更多内容。这是一个交互式演示文稿,解释了很多关于 JavaScript 的内容,示例读起来很有趣,执行起来也很有趣。

它有一章关于闭包,这个例子看起来很像你的。

这是损坏的示例:

var count = 0; 
for ( var i = 0; i < 4; i++ ) { 
  setTimeout(function(){ 
    assert( i == count++, "Check the value of i." ); 
  }, i * 200); 
}

和修复:

var count = 0; 
for ( var i = 0; i < 4; i++ ) (function(i){ 
  setTimeout(function(){ 
    assert( i == count++, "Check the value of i." ); 
  }, i * 200); 
})(i);