JavaScript闭包与匿名函数


562

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

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

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

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

当然,它将i在循环后打印10倍的值,即10。

因此,他的尝试是:

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

按预期打印0到9。

我告诉他,他并没有使用闭包捕获i,但他坚持认为自己是。我通过将for循环体放在另一个循环中(将他的匿名函数传递给),再次打印10次10 证明了他没有使用闭包。如果我将他的函数存储在a中并在循环执行它,也打印10次10​​,则同样适用。所以我的观点是他并没有真正捕获的值,因此他的版本不是闭包的。setTimeoutsetTimeoutvari

我的尝试是:

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

所以我捕获了ii2在闭包中命名),但是现在我返回了另一个函数并传递了它。就我而言,传递给setTimeout的函数实际上捕获了i

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

请注意,这两个解决方案在控制台上都延迟输出0到9,因此它们可以解决原始问题,但是我们想了解这两个解决方案中的哪个使用闭包来完成此任务。



1
@leemes:请参阅我的忍者编辑以获得第二个链接。
Blender 2012年

2
我们刚刚达成了一个协议:正确的人将获得与这个问题有关的SO点
出色的表现2012年

1
@leemes-你们俩都在使用闭包。你们两个都有两个功能-一个外部功能和一个内部功能;而您的两个内部函数都是闭包。您的所有函数都是lambdas(匿名函数)。阅读我的答案以获取详细信息。
Aadit M Shah 2012年

1
@blesh-我不知道什么是修改的闭包。我看到您的链接指向C#代码。JavaScript支持修改的闭包吗?
Aadit M Shah 2012年

Answers:


649

编者按:在JavaScript中所有的功能都关闭在这个解释。但是,我们只对确定这些功能的子集感兴趣,而这些子集从理论上讲是有趣的。此后,除非另有说明,否则对闭包一词的任何引用都将指代此功能子集。

闭包的简单说明:

  1. 发挥作用。我们称它为F。
  2. 列出F的所有变量。
  3. 变量可以有两种类型:
    1. 局部变量(绑定变量)
    2. 非局部变量(自由变量)
  4. 如果F没有自由变量,则它不能是闭包。
  5. 如果F具有任何自由变量(在F 父范围中定义),则:
    1. 必须有其中的F只有一个父范围一个自由变量绑定。
    2. 如果从父范围之外引用 F ,则它将成为自由变量的闭包。
    3. 自由变量称为闭包F的高值。

现在让我们用它来找出谁使用闭包,谁不使用闭包(为便于说明,我将函数命名为):

情况1:您朋友的程序

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

在以上程序中,有两个功能:fg。让我们看看它们是否为闭包:

对于f

  1. 列出变量:
    1. i2局部变量。
    2. i是一个自由变量。
    3. setTimeout是一个自由变量。
    4. g局部变量。
    5. console是一个自由变量。
  2. 找到每个自由变量绑定到的父作用域:
    1. i绑定到了全球范围。
    2. setTimeout绑定到了全球范围。
    3. console绑定到了全球范围。
  3. 该功能在哪个范围内引用?在全球范围内
    1. 因此i没有关闭了通过f
    2. 因此setTimeout没有关闭了通过f
    3. 因此console没有关闭了通过f

因此,该功能f不是闭包。

对于g

  1. 列出变量:
    1. console是一个自由变量。
    2. i2是一个自由变量。
  2. 找到每个自由变量绑定到的父作用域:
    1. console绑定到了全球范围。
    2. i2绑定到的范围f
  3. 该功能在哪个范围内引用?的范围setTimeout
    1. 因此console没有关闭了通过g
    2. 因此i2封闭在通过g

因此,该功能g是用于自由变量的封闭i2(这是用于的upvalue g它的引用从内setTimeout

对您不利:您的朋友正在使用闭包。内部函数是一个闭包。

情况2:您的程序

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

在以上程序中,有两个功能:fg。让我们看看它们是否为闭包:

对于f

  1. 列出变量:
    1. i2局部变量。
    2. g局部变量。
    3. console是一个自由变量。
  2. 找到每个自由变量绑定到的父作用域:
    1. console绑定到了全球范围。
  3. 该功能在哪个范围内引用?在全球范围内
    1. 因此console没有关闭了通过f

因此,该功能f不是闭包。

对于g

  1. 列出变量:
    1. console是一个自由变量。
    2. i2是一个自由变量。
  2. 找到每个自由变量绑定到的父作用域:
    1. console绑定到了全球范围。
    2. i2绑定到的范围f
  3. 该功能在哪个范围内引用?的范围setTimeout
    1. 因此console没有关闭了通过g
    2. 因此i2封闭在通过g

因此,该功能g是用于自由变量的封闭i2(这是用于的upvalue g它的引用从内setTimeout

对您有好处:您正在使用闭包。内部函数是一个闭包。

因此,您和您的朋友都在使用闭包。别吵了 我希望我清除了闭包的概念以及如何为你们两个人识别它们。

编辑:关于为什么所有函数都关闭的简单说明(@Peter,鸣谢):

首先让我们考虑以下程序(它是control):

lexicalScope();

function lexicalScope() {
    var message = "This is the control. You should be able to see this message being alerted.";

    regularFunction();

    function regularFunction() {
        alert(eval("message"));
    }
}

  1. 我们知道,无论lexicalScoperegularFunction不封闭,从上面的定义
  2. 当我们执行程序时,我们希望 message收到警告,因为 regularFunction它不是闭包的(即它可以访问其父范围内的所有变量-包括message)。
  3. 当我们执行程序时,我们发现message确实确实有警报。

接下来,让我们考虑以下程序(它是替代程序):

var closureFunction = lexicalScope();

closureFunction();

function lexicalScope() {
    var message = "This is the alternative. If you see this message being alerted then in means that every function in JavaScript is a closure.";

    return function closureFunction() {
        alert(eval("message"));
    };
}

  1. 我们知道,这只是上述定义closureFunction的闭包。
  2. 当我们执行程序时,我们希望 message不会因为 closureFunction闭包而收到警告(即,在创建函数时,它只能访问其所有非局部变量请参见此答案)-不包括)。message
  3. 当我们执行程序时,我们观察message实际上正在被警告。

我们从中得出什么呢?

  1. JavaScript解释器对待闭包的方式与对待其他函数的方式没有区别。
  2. 每个功能都带有其作用域链。闭包没有单独的引用环境。
  3. 闭包就像其他函数一样。当在它们所属的范围之外的范围中引用它们时,我们只称它们为闭包,因为这是一个有趣的情况。

40
被接受,因为您非常详细,解释了所发生的一切。最后,我现在更好地了解了什么是闭包,或者说得更好:变量绑定在JS中的工作方式。
leemes 2012年

3
在案例1中,您说g在范围内运行setTimeout,但在案例2中,您说f在全局范围内运行。它们都在setTimeout内,所以有什么区别?
rosscj2533

9
请问您的来源吗?我从未见过这样的定义:如果在一个作用域而不是在另一个作用域中调用函数,则该函数可以是闭包。因此,此定义似乎是我惯用的更笼统定义的子集(请参阅kev的答案),其中闭包是闭包是闭包,无论其被调用的范围是什么,甚至从未被调用!
Briguy37

11
@AaditMShah我同意您对闭包的看法,但是您说的似乎是常规函数和JavaScript中的闭包之间存在差异。没有区别; 在内部,每个函数都将带有对创建它的特定作用域链的引用。JS引擎不会将其视为其他情况。不需要复杂的清单。只知道每个函数对象都带有词法范围。变量/属性是全局可用的事实并不能使函数成为闭包(这只是一个无用的情况)。
彼得

13
@Peter-你知道吗,你是正确的。常规函数和闭包之间没有区别。我进行了测试以证明这一点,并得到您的青睐:这是控件,这是替代方法。你说的确实有道理。JavaScript解释器需要对关闭进行特殊的簿记。它们只是具有一流功能的词汇范围语言的副产品。我的知识仅限于我阅读的内容(这是错误的)。谢谢你纠正我。我将更新我的答案以反映相同的内容。
Aadit M Shah 2012年

96

根据closure定义:

“闭包”是一个表达式(通常是一个函数),可以将自由变量与绑定这些变量的环境(“封闭”表达式)一起使用。

您正在使用closure是否定义了一个函数,该函数使用在函数外部定义的变量。(我们将该变量称为自由变量)。
它们都使用closure(甚至在第一个示例中)。


1
第三个版本如何使用在函数外部定义的变量?
2012年

1
@Jon在i2外部定义的返回函数用法。
kev 2012年

1
@kev如果您定义的函数使用的是在函数外部定义的变量,则您正在使用闭包...然后,“ Aadit M Shah”的“案例1:您的朋友的程序”中的答案是“函数f”封闭?它使用i(在函数外部定义的变量)。全局范围引用确定符吗?
内部-在


54

一言以蔽之的Javascript闭包允许函数访问的变量在词法父函数声明

让我们看一个更详细的解释。要了解闭包,重要的是要了解JavaScript如何作用域变量。

范围

在JavaScript中,作用域是用函数定义的。每个函数定义一个新的范围。

考虑下面的例子;

function f()
{//begin of scope f
  var foo='hello'; //foo is declared in scope f
  for(var i=0;i<2;i++){//i is declared in scope f
     //the for loop is not a function, therefore we are still in scope f
     var bar = 'Am I accessible?';//bar is declared in scope f
     console.log(foo);
  }
  console.log(i);
  console.log(bar);
}//end of scope f

调用f打印

hello
hello
2
Am I Accessible?

现在让我们考虑g在另一个函数中定义一个函数的情况f

function f()
{//begin of scope f
  function g()
  {//being of scope g
    /*...*/
  }//end of scope g
  /*...*/
}//end of scope f

我们将调用f词汇父g。如前所述,我们现在有2个作用域;范围f和范围g

但是一个范围在另一个范围内,那么子功能的范围是否是父功能范围的一部分?在父函数范围内声明的变量会发生什么;我可以从子功能的范围访问它们吗?这正是闭包介入的地方。

关闭

在JavaScript中,该函数g不仅可以访问在scope中声明的任何变量,g而且还可以访问在父函数的scope中声明的任何变量f

考虑以下;

function f()//lexical parent function
{//begin of scope f
  var foo='hello'; //foo declared in scope f
  function g()
  {//being of scope g
    var bar='bla'; //bar declared in scope g
    console.log(foo);
  }//end of scope g
  g();
  console.log(bar);
}//end of scope f

调用f打印

hello
undefined

让我们看看这条线console.log(foo);。至此,我们进入了作用域,g并尝试访问foo在作用域中声明的变量f。但是如前所述,我们可以访问在词法父函数中声明的任何变量。g是。的词汇父f。因此hello被打印。
现在让我们看一下这行console.log(bar);。至此,我们进入了作用域f,我们尝试访问bar在作用域中声明的变量gbar未在当前作用域中声明,并且该函数g不是的父项f,因此bar未定义

实际上,我们还可以访问在词法“祖父”函数范围内声明的变量。因此,如果有一个功能h内定义了一个函数g

function f()
{//begin of scope f
  function g()
  {//being of scope g
    function h()
    {//being of scope h
      /*...*/
    }//end of scope h
    /*...*/
  }//end of scope g
  /*...*/
}//end of scope f

然后h将能够访问所有的功能范围内声明的变量hgf。这是通过闭包完成的。在JavaScript中,闭包允许我们访问在词法父函数,词法大父函数,词法大祖父函数等中声明的任何变量。这可以看作是一个作用域链 scope of current function -> scope of lexical parent function -> scope of lexical grand parent function -> ... 直到没有词法父级的最后一个父级函数。

窗口对象

实际上,链条不会在最后一个父函数处停止。还有一个特殊的范围;在全球范围内。未在函数中声明的每个变量都被视为在全局范围内声明。全局范围有两个专长:

  • 全局范围内声明的每个变量都可以在任何地方访问
  • 在全局范围内声明的变量对应于window对象的属性。

因此,有两种foo在全局范围内声明变量的方法:通过不在函数中声明它或通过设置foo窗口对象的属性来实现。

两种尝试都使用闭包

现在,您已经阅读了更详细的说明,现在显而易见,两种解决方案都使用闭包。但是可以肯定的是,让我们做个证明。

让我们创建一种新的编程语言;JavaScript不公开。顾名思义,JavaScript-No-Closure与JavaScript相同,只是不支持Closures。

换一种说法;

var foo = 'hello';
function f(){console.log(foo)};
f();
//JavaScript-No-Closure prints undefined
//JavaSript prints hello

好吧,让我们看看第一个使用JavaScript-No-Closure的解决方案会发生什么;

for(var i = 0; i < 10; i++) {
  (function(){
    var i2 = i;
    setTimeout(function(){
        console.log(i2); //i2 is undefined in JavaScript-No-Closure 
    }, 1000)
  })();
}

因此,这将undefined在JavaScript无提示中打印10次​​。

因此,第一个解决方案使用闭包。

让我们看第二种解决方案;

for(var i = 0; i < 10; i++) {
  setTimeout((function(i2){
    return function() {
        console.log(i2); //i2 is undefined in JavaScript-No-Closure
    }
  })(i), 1000);
}

因此,这将undefined在JavaScript无提示中打印10次​​。

两种解决方案都使用闭包。

编辑:假定这3个代码段未在全局范围内定义。否则,变量fooi将绑定到该window对象,并因此通过访问window在JavaScript和JavaScript的无关闭对象。


为什么要i不确定?您仅引用父作用域,如果没有闭包,它仍然有效。
leemes 2012年

出于与foo相同的原因,JavaScript-No-Closure中未定义foo。<code> i </ code>在JavaScript中不是未定义的,这要归功于JavaScript中的一项功能,该功能允许访问在词法父级中定义的变量。此功能称为关闭。
brillout 2012年

您不了解引用已定义的变量和自由变量之间的区别。在闭包中,我们定义了必须在外部上下文中绑定的自由变量。在代码中,您只需在定义函数时将其设置 i2为即可i。这i不是NOT自由变量。尽管如此,我们仍将您的函数视为闭包,但是没有任何自由变量,这才是重点。
leemes

2
@leemes,我同意。与接受的答案相比,这并没有真正显示出实际情况。:)
Abel

3
我认为这是最好的答案,通常简单地解释闭包,然后进入特定的用例。谢谢!
蒂姆·彼得森2013年

22

我从来不满意任何人对此进行解释的方式。

理解闭包的关键是了解没有闭包的JS的样子。

没有闭包,这将引发错误

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

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

一旦externalFunc返回了一个假想的禁用禁用的JavaScript版本,对外部变量的引用将被垃圾回收并且不留任何内容供内部函数进行引用。

闭包本质上是特殊的规则,当内部函数引用外部函数的变量时,闭包可以使这些var存在。使用闭包,即使在完成外部函数之后也将保留引用的变量,如果可以帮助您记住要点,则可以“关闭”。

即使使用闭包,没有内部函数引用其局部变量的函数中局部变量的生命周期也与无闭合版本中的局部变量相同。功能完成后,本地人将收集垃圾。

一旦在内部函数中对外部var进行了引用,那么就好比对于那些引用的var来说,门禁将被垃圾回收。

查看闭包的一种可能更准确的方法是,内部函数基本上将内部范围用作其自身的范围基础。

但是引用的上下文实际上是持久的,而不是快照。反复触发返回的内部函数,该内部函数将不断递增并记录外部函数的本地var,将继续提醒更高的值。

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

你对“快照”是正确的(我想,你指的是我的答案)。我一直在寻找一个能说明问题的单词。在您的示例中,可以将其视为“ hotlink”关闭结构。当在内部函数中将闭包作为参数捕获时,可以声明其表现为“快照”。但是我同意,误用词只会使这个话题更加混乱。如果您对此有任何建议,我将更新我的答案。
Andries 2012年

如果您将内部函数指定为命名函数,则可能有助于解释。
菲利普·森

如果不使用闭包,则会因为尝试使用不存在的变量而出现错误。
Juan Mendes

嗯...好点。引用未定义的var是否不会抛出错误,因为它最终将作为全局对象的属性来查找,还是我对分配给未定义的var感到困惑?
埃里克·雷彭

17

你们都在使用闭包。

我在这里使用Wikipedia定义

在计算机科学中,闭包(也称为词法闭包或函数闭包)是函数或对函数的引用以及引用环境,即一个表,该表存储对该函数的每个非局部变量(也称为自由变量)的引用。与普通函数指针不同,闭包允许函数访问那些非局部变量,即使在其直接词法范围之外调用该函数也是如此。

您朋友的尝试i通过获取值并将其副本存储到local中,显然使用了非local 变量i2

您自己的尝试i(在调用站点的范围内)传递给匿名函数作为参数。到目前为止,这还不是一个闭包,但是该函数将返回另一个引用了same的函数i2。由于内部匿名函数内部i2不是本地函数,因此将创建一个闭包。


是的,但我认为这一点是如何,他是做什么的。他只是复制ii2,然后定义一些逻辑并执行此功能。如果我不立即执行它,而是将其存储在var中,并在循环后执行,它将打印10,不是吗?所以它并没有抓住我。
leemes 2012年

6
@leemes:它i很好捕获。您描述的行为不是闭包与非闭包的结果;这是因为同时更改了封闭变量的结果。通过立即调用函数并i作为参数传递(即刻复制其当前值),您正在使用不同的语法执行相同的操作。如果您将自己setTimeout放置在另一个内部setTimeout,则将发生同样的事情。
2012年

13

您和您的朋友都使用闭包:

闭包是一种特殊的对象,它结合了两件事:一个函数以及创建该函数的环境。该环境由创建闭包时在范围内的任何局部变量组成。

MDN:https//developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures

在您朋友的代码函数function(){ console.log(i2); }内部定义的闭包匿名函数中function(){ var i2 = i; ...,可以读写局部变量i2

在您的代码函数中function(){ console.log(i2); }定义的函数关闭内部function(i2){ return ...并可以读/写本地有价值的东西i2(在这种情况下声明为参数)。

在这两种情况下,函数都function(){ console.log(i2); }传递给setTimeout

另一个等效项(但内存利用率较低)是:

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

1
我不明白为什么您的解决方案与我朋友的解决方案“更快,内存利用率更低”,您能否详细说明一下?
brillout 2012年

3
在您的解决方案中,您将创建20个功能对象(每个循环2个对象:2x10 = 20)。解决您的朋友的结果相同。在“我的”解决方案中,仅创建11个功能对象:1个用于for循环,10个“内部”-1 + 1x10 = 11。结果-减少了内存使用并提高了速度。
Andrew D.

1
从理论上讲,这是正确的。在实践中,还:请参见此JSPerf基准测试:jsperf.com/closure-vs-name-function-in-a-loop/2
Rob W

10

关闭

闭包不是函数,也不是表达式。必须将其视为来自功能范围外部和功能内部使用的变量的“快照”。在语法上,一个人应该说:'采取封闭的变量'。

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

再一次(天真):闭包可以访问未作为参数传递的变量。

请记住,这些功能概念在很大程度上取决于您使用的编程语言/环境。在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)
}

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



10

让我们看一下两种方式:

(function(){
    var i2 = i;
    setTimeout(function(){
        console.log(i2);
    }, 1000)
})();

声明并立即执行setTimeout()在其自身上下文中运行的匿名函数。的当前值i是通过将副本复制为i2first 来保留的;它之所以有效是因为立即执行。

setTimeout((function(i2){
    return function() {
        console.log(i2);
    }
})(i), 1000);

声明内部函数的执行上下文,由此将的当前值i保存到i2;这种方法还使用立即执行来保留值。

重要

应当指出的是,两种方法之间的运行语义是不同的。你的内部函数被传递给setTimeout()他的内部函数setTimeout()自己。

将两个代码包装在另一个内部 setTimeout()并不能证明只有第二种方法使用了闭包,开始的东西并不相同。

结论

两种方法都使用闭包,所以这取决于个人喜好。第二种方法更容易“移动”或概括。


我认为区别在于:他的解决方案(第一)是通过引用捕获的,我的解决方案(第二)是通过值捕获的。在这种情况下,它没有什么区别,但是如果我将执行放入另一个setTimeout中,我们将看到他的解决方案存在一个问题,那就是它使用了最终值i而不是当前值,而矿山使用了当前值(由于按值捕获)。
leemes 2012年

@leemes你们都用相同的方式捕获;通过函数参数或赋值传递变量是同一回事……您能不能添加一个将执行结果包装在另一个变量中的问题setTimeout()
杰克

让我检查一下...我想证明函数对象可以传递并且i可以更改原始变量,而不会影响函数的输出内容,而不取决于我们在哪里执行或何时执行。
leemes 2012年

等等,您没有将函数传递给(外部)setTimeout。删除那些(),从而传递一个函数,您将看到10倍的输出10
leemes 2012年

@leemes如前所述,()正是使他的代码起作用的原因,就像您的(i); 一样。您不仅包装了他的代码,还对其进行了更改..因此,您无法再进行有效的比较。
杰克

8

我前一段时间写这是为了提醒自己什么是闭包以及它在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' 

6

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

在您的朋友的情况下,i可在匿名函数1 i2中访问,而在匿名函数2中访问,其中console.log

在您的情况下,您正在访问i2内部console.log存在的匿名函数。在chrome开发人员工具的“作用域变量”下和debugger;之前添加一条语句console.log,它将告诉变量该变量在什么作用域下。


2
使用右侧面板上的“关闭”部分,因为没有更具体的名称。“本地”比“关闭”更强。
罗伯W


4

考虑以下。这将创建并重新创建一个f在上关闭i但不同的函数!:

i=100;

f=function(i){return function(){return ++i}}(0);
alert([f,f(),f(),f(),f(),f(),f(),f(),f(),f(),f()].join('\n\n'));

f=function(i){return new Function('return ++i')}(0);        /*  function declarations ~= expressions! */
alert([f,f(),f(),f(),f(),f(),f(),f(),f(),f(),f()].join('\n\n'));

而以下内容关闭“ a”函数“自身”
(自己!此后的代码段使用单个引用对象f

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

或更明确地说:

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

注意 最后定义ffunction(){ console.log(9) } 0

警告!闭包概念可能会强制干扰基本编程的本质:

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

x-refs.:JavaScript
闭包如何工作?
Javascript闭包说明
(JS)闭是否需要函数内部的函数
如何理解Java闭包?
Javascript局部和全局变量混淆


片段第一次尝试-不知道如何控制- Run' only was desired - not sure how to remove the 复制`
ekim 2015年

-1

我想分享我的例子和关于闭包的解释。我制作了一个python示例,并用两个数字演示了堆栈状态。

def maker(a, b, n):
    margin_top = 2
    padding = 4
    def message(msg):
        print('\n * margin_top, a * n, 
            ' ‘ * padding, msg, '  * padding, b * n)
    return message

f = maker('*', '#', 5)
g = maker('', '♥’, 3)

f('hello')
g(‘good bye!')

此代码的输出如下:

*****      hello      #####

      good bye!    ♥♥♥

这是两个图,显示了堆栈和连接到函数对象的闭包。

从制造商返回功能时

稍后调用该函数时

通过参数或非局部变量调用该函数时,代码需要局部变量绑定,例如margin_top,padding以及a,b,n。为了确保功能代码正常工作,应该访问很早就消失的maker函数的堆栈框架,该框架在我们可以与功能消息对象一起找到的闭包中进行备份。


我想删除此答案。我意识到问题不在于什么是闭包,因此我想将其移至另一个问题。
李恩荣

2
我相信您可以删除自己的内容。单击delete答案下面的链接。
罗里·麦克罗桑
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.