我的一个朋友和我正在讨论什么是JS中的闭包,什么不是。我们只是想确保我们理解正确。

让我们举个例子。我们有一个计数循环,并希望在控制台上延迟打印计数器变量。因此,我们使用setTimeout和闭包来捕获计数器变量的值,以确保它不会打印N倍的值。

没有闭包或接近闭包的错误解决方案是:

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

这当然会在循环后输出10倍I的值,也就是10。

所以他的尝试是:

for(var i = 0; i < 10; i++) {
    (function(){
        var i2 = i;
        setTimeout(function(){
            console.log(i2);
        }, 1000)
    })();
}

按预期打印0到9。

我告诉他,他没有使用闭包来捕获I,但他坚持说他是。我通过将for循环体放在另一个setTimeout(将他的匿名函数传递给setTimeout)中,再次打印10乘以10来证明他没有使用闭包。如果我将他的函数存储在var中,并在循环之后执行它,同样打印10乘以10。所以我的论点是,他没有真正捕获i的值,使得他的版本不是一个闭包。

我的尝试是:

for(var i = 0; i < 10; i++) {
    setTimeout((function(i2){
        return function() {
            console.log(i2);
        }
    })(i), 1000);
}

所以我捕获了I(在闭包中命名为i2),但现在我返回另一个函数并传递它。在我的例子中,传递给setTimeout的函数实际上捕获了i。

现在谁在使用闭包,谁没有呢?

请注意,这两个解决方案都延迟在控制台上打印0到9,因此它们解决了最初的问题,但我们想了解这两个解决方案中哪一个使用闭包来完成这一任务。


当前回答

我对任何人解释这件事的方式都不满意。

理解闭包的关键是理解没有闭包的JS会是什么样子。

如果没有闭包,这将抛出一个错误

function outerFunc(){
    var outerVar = 'an outerFunc var';
    return function(){
        alert(outerVar);
    }
}

outerFunc()(); //returns inner function and fires it

一旦outerFunc在一个假想的禁用闭包的JavaScript版本中返回,对outerVar的引用将被垃圾收集并消失,没有留下任何东西供内部func引用。

闭包本质上是一种特殊的规则,当内部函数引用外部函数的变量时,闭包可以使这些变量存在。使用闭包,即使在外部函数完成或“关闭”之后,引用的vars也会被维护,如果这有助于您记住要点的话。

即使使用闭包,在没有引用局部函数的内部函数的函数中,局部变量的生命周期也与在无闭包版本中工作相同。当活动结束时,当地人会收集垃圾。

一旦你在一个内部的func中引用了一个外部的变量,然而,它就像一个门框被放置在垃圾收集的方式为那些引用的变量。

看待闭包的一个可能更准确的方法是,内部函数基本上使用内部作用域作为自己的作用域基础。

但是引用的上下文实际上是持久的,不像快照。反复触发一个不断递增的内部函数,并记录外部函数的局部变量,将不断警告更高的值。

function outerFunc(){
    var incrementMe = 0;
    return function(){ incrementMe++; console.log(incrementMe); }
}
var inc = outerFunc();
inc(); //logs 1
inc(); //logs 2

其他回答

关闭

闭包不是函数,也不是表达式。它必须被视为一种从函数作用域外使用的变量到函数内部使用的“快照”。从语法上讲,我们应该说:“取变量的闭包”。

同样,换句话说:闭包是函数所依赖的变量的相关上下文的副本。

再说一次(naïf):闭包可以访问没有作为参数传递的变量。

请记住,这些函数概念在很大程度上取决于所使用的编程语言/环境。在JavaScript中,闭包依赖于词法作用域(这在大多数c语言中是正确的)。

返回一个函数主要是返回一个匿名/未命名函数。当函数访问变量时,没有作为参数传递,并且在其(词法)范围内,则采用闭包。

关于你的例子:

// 1
for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i); // closure, only when loop finishes within 1000 ms,
    }, 1000);           // i = 10 for all functions
}
// 2
for(var i = 0; i < 10; i++) {
    (function(){
        var i2 = i; // closure of i (lexical scope: for-loop)
        setTimeout(function(){
            console.log(i2); // closure of i2 (lexical scope:outer function)
        }, 1000)
    })();
}
// 3
for(var i = 0; i < 10; i++) {
    setTimeout((function(i2){
        return function() {
            console.log(i2); // closure of i2 (outer scope)

        }
    })(i), 1000); // param access i (no closure)
}

所有都使用闭包。不要将执行点与闭包混淆。如果闭包的“快照”是在错误的时刻拍摄的,值可能是意外的,但肯定是一个闭包!

在仔细检查之后,看起来你们都在使用封闭。

在你的朋友的例子中,i在匿名函数1中被访问,i2在匿名函数2中被访问,其中console.log是存在的。

在您的情况下,您正在访问匿名函数中的i2,其中console.log存在。添加调试器;在console.log和chrome开发工具的“作用域变量”下,它会告诉变量的作用域。

我之前写这篇文章是为了提醒自己闭包是什么以及它在JS中是如何工作的。

闭包是一种函数,它在被调用时使用声明它的作用域,而不是调用它的作用域。在javaScript中,所有函数都是这样的。只要函数仍然指向作用域中的变量值,作用域中的变量值就会持续存在。该规则的例外是'this',它指的是函数被调用时所在的对象。

var z = 1;
function x(){
    var z = 2; 
    y(function(){
      alert(z);
    });
}
function y(f){
    var z = 3;
    f();
}
x(); //alerts '2' 

根据闭包定义:

“闭包”是一个表达式(通常是一个函数),它可以有自由变量和绑定这些变量的环境(“关闭”表达式)。

如果你定义的函数使用了一个在函数外部定义的变量,那么你就是在使用闭包。(我们称这个变量为自由变量)。 它们都使用闭包(甚至在第一个例子中)。

考虑以下几点。 这创建并重新创建了一个函数f,它对i关闭,但是不同的函数!: 我= 100; F =函数(i){返回函数(){返回++i}}(0); 警报([f, f (), f (), f (), f (), f (), f (), f (), f (), f (), f ()] . join (' \ n \ n ')); f=function(i){return new function('return ++i')}(0);/*函数声明~=表达式!* / 警报([f, f (), f (), f (), f (), f (), f (), f (), f (), f (), f ()] . join (' \ n \ n '));

当下面的语句关闭a函数本身时 (自己!之后的代码段使用了一个单独的引用f) For (var I = 0;I < 10;我+ +){ setTimeout(new Function('console.log('+i+')'), 1000); }

或者更明确地说:

For (var I = 0;I < 10;我+ +){ console.log(f = new Function('console.log('+i+')')); setTimeout(f, 1000); }

NB。f的最后一个定义是打印0之前的function(){console.log(9)}。

警告!闭包的概念可以强制地偏离初级编程的本质:

for(var i = 0;i < 10;i++)(“timeout”(“log”),1000);)

x-refs。 JavaScript闭包是如何工作的? Javascript闭包说明 一个(JS)闭包需要函数内部的函数吗 如何理解闭包在Javascript? Javascript局部变量和全局变量混淆