您将如何向了解其闭包概念(例如函数,变量等)的人解释JavaScript闭包,但却不了解闭包本身?

我已经在Wikipedia上看到了Scheme示例 ,但是不幸的是它没有帮助。


#1楼

我知道已经有很多解决方案,但是我猜想这个小而简单的脚本可以用来说明这个概念:

// makeSequencer will return a "sequencer" function
var makeSequencer = function() {var _count = 0; // not accessible outside this functionvar sequencer = function () {return _count++;}return sequencer;
}var fnext = makeSequencer();
var v0 = fnext();     // v0 = 0;
var v1 = fnext();     // v1 = 1;
var vz = fnext._count // vz = undefined

#2楼

面向初学者的JavaScript关闭

莫里斯在2006年2月2日星期二提交。 从此开始由社区编辑。

关闭不是魔术

本页说明了闭包,以便程序员可以使用有效的JavaScript代码来理解闭包。 它不适用于专家或功能性程序员。

一旦核心概念浮出水面,关闭就不难理解。 但是,通过阅读任何理论或学术上的解释是不可能理解它们的!

本文面向具有某种主流语言编程经验并且可以阅读以下JavaScript函数的程序员:

 function sayHello(name) { var text = 'Hello ' + name; var say = function() { console.log(text); } say(); } sayHello('Joe'); 

两篇摘要

  • 当一个函数( foo )声明其他函数(bar和baz)时,在该函数退出时,在foo创建的局部变量族不会被破坏 。 这些变量只是对外界不可见。 因此, foo可以巧妙地返回功能barbaz ,并且它们可以通过这个封闭的变量家族(“闭包”)继续进行读取,写入和通信,这些变量是其他任何人都无法干预的,甚至没有人可以介入foo再来一次。

  • 闭包是支持一流功能的一种方式。 它是一个表达式,可以引用其范围内的变量(首次声明时),分配给变量,作为参数传递给函数或作为函数结果返回。

闭包的例子

以下代码返回对函数的引用:

 function sayHello2(name) { var text = 'Hello ' + name; // Local variable var say = function() { console.log(text); } return say; } var say2 = sayHello2('Bob'); say2(); // logs "Hello Bob" 

大多数JavaScript程序员将理解上面代码中如何将对函数的引用返回到变量( say2 )。 如果不这样做,那么您需要先研究一下闭包。 使用C的程序员会将函数视为返回函数的指针,并且变量saysay2分别是函数的指针。

指向函数的C指针和指向函数的JavaScript引用之间存在关键区别。 在JavaScript中,你可以把一个函数引用变量作为既具有指针功能以及一个隐藏的指针关闭。

上面的代码已关闭,因为匿名函数function() { console.log(text); } function() { console.log(text); } 另一个函数内部声明,在此示例中为sayHello2() 。 在JavaScript中,如果在另一个函数中使用function关键字,则将创建一个闭包。

在C和其他大多数常用语言,函数返回 ,因为堆栈帧被摧毁了所有的局部变量都不再使用。

在JavaScript中,如果在另一个函数中声明一个函数,则外部函数从其返回后仍可访问。 上面已经演示了这一点,因为我们从sayHello2()返回后调用了函数say2() sayHello2() 。 请注意,我们调用的代码引用了变量text ,这是函数sayHello2()局部变量

function() { console.log(text); } // Output of say2.toString();

查看say2.toString()的输出,我们可以看到该代码引用了变量text 。 匿名函数可以引用包含值'Hello Bob' text ,因为sayHello2()的局部变量已在闭包中秘密保持活动状态。

天才之处在于,在JavaScript中,函数引用还具有对其创建于其内的闭包的秘密引用—类似于委托如何成为方法指针以及对对象的秘密引用。

更多例子

由于某些原因,当您阅读闭包时,似乎真的很难理解它,但是当您看到一些示例时,很清楚它们是如何工作的(花了我一段时间)。 我建议仔细研究这些示例,直到您理解它们的工作原理。 如果您在不完全了解闭包工作原理的情况下开始使用闭包,那么您很快就会创建一些非常奇怪的错误!

例子3

此示例显示局部变量未复制-通过引用保留它们。 似乎即使外部函数退出后,堆栈框架仍在内存中保持活动状态!

 function say667() { // Local variable that ends up within closure var num = 42; var say = function() { console.log(num); } num++; return say; } var sayNumber = say667(); sayNumber(); // logs 43 

例子4

这三个全局函数都对同一闭包有共同的引用,因为它们都在一次调用setupSomeGlobals()

 var gLogNumber, gIncreaseNumber, gSetNumber; function setupSomeGlobals() { // Local variable that ends up within closure var num = 42; // Store some references to functions as global variables gLogNumber = function() { console.log(num); } gIncreaseNumber = function() { num++; } gSetNumber = function(x) { num = x; } } setupSomeGlobals(); gIncreaseNumber(); gLogNumber(); // 43 gSetNumber(5); gLogNumber(); // 5 var oldLog = gLogNumber; setupSomeGlobals(); gLogNumber(); // 42 oldLog() // 5 

这三个函数具有对同一个闭包的共享访问权限-定义了三个函数时, setupSomeGlobals()的局部变量。

请注意,在上面的示例中,如果再次调用setupSomeGlobals() ,则会创建一个新的闭包(堆栈框架!)。 旧的gLogNumbergIncreaseNumbergSetNumber变量将被具有新闭包的函数覆盖。 (在JavaScript中,每当在另一个函数中声明一个函数时,每次调用外部函数时都会重新创建一个 (或多个)内部函数。)

例子5

此示例显示闭包包含退出前在外部函数内部声明的任何局部变量。 请注意,变量alice实际上是在匿名函数之后声明的。 首先声明匿名函数,并在调用该函数时可以访问alice变量,因为alice在同一作用域内(JavaScript进行变量提升 )。 同样, sayAlice()()只是直接调用从sayAlice()返回的函数引用-它与之前所做的操作完全相同,但没有临时变量。

 function sayAlice() { var say = function() { console.log(alice); } // Local variable that ends up within closure var alice = 'Hello Alice'; return say; } sayAlice()();// logs "Hello Alice" 

棘手:请注意, say变量也位于闭包内部,可以由在sayAlice()声明的任何其他函数访问,或者可以在内部函数中递归访问。

例子6

对于许多人来说,这是一个真正的陷阱,因此您需要了解它。 如果要在循环内定义函数,请非常小心:闭包中的局部变量可能不会像您首先想到的那样起作用。

您需要了解Javascript中的“变量提升”功能才能了解此示例。

 function buildList(list) { var result = []; for (var i = 0; i < list.length; i++) { var item = 'item' + i; result.push( function() {console.log(item + ' ' + list[i])} ); } return result; } function testList() { var fnlist = buildList([1,2,3]); // Using j only to help prevent confusion -- could use i. for (var j = 0; j < fnlist.length; j++) { fnlist[j](); } } testList() //logs "item2 undefined" 3 times 

result.push( function() {console.log(item + ' ' + list[i])}会在结果数组中添加对匿名函数的引用三次,如果您不太熟悉匿名函数,请考虑一下就如:

pointer = function() {console.log(item + ' ' + list[i])};
result.push(pointer);

请注意,在运行示例时, "item2 undefined"被记录了三次! 这是因为就像前面的示例一样, buildList的局部变量( resultilistitem )只有一个闭包。 当在行上调用匿名函数fnlist[j]() ; 它们都使用相同的单个闭包,并且使用该闭包中iitem的当前值(其中i的值为3因为循环已完成, item的值为'item2' )。 注意,我们从0开始索引,因此item的值为item2 。 i ++将i递增到值3

查看使用变量item的块级声明(通过let关键字)而不是通过var关键字进行函数范围的变量声明时,会发生什么。 如果进行了更改,则数组result中的每个匿名函数都有其自己的关闭; 运行示例时,输出如下:

item0 undefined
item1 undefined
item2 undefined

如果变量i也是使用let而不是var定义的,则输出为:

item0 1
item1 2
item2 3

例子7

在最后一个示例中,对主函数的每次调用都会创建一个单独的闭包。

 function newClosure(someNum, someRef) { // Local variables that end up within closure var num = someNum; var anArray = [1,2,3]; var ref = someRef; return function(x) { num += x; anArray.push(num); console.log('num: ' + num + '; anArray: ' + anArray.toString() + '; ref.someVar: ' + ref.someVar + ';'); } } obj = {someVar: 4}; fn1 = newClosure(4, obj); fn2 = newClosure(5, obj); // attention here: new closure assigned to a new variable! fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4; fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4; obj.someVar++; fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5; fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5; 

摘要

如果一切似乎都不清楚,那么最好的办法就是看这些例子。 阅读说明比理解示例困难得多。 我对闭包和堆栈框架等的解释在技术上并不正确-它们是旨在帮助理解的粗略简化。 一旦了解了基本概念,您便可以稍后进行详细了解。

最后一点:

  • 每当在另一个函数中使用function ,都会使用闭包。
  • 每当在eval()内部使用eval() ,都会使用闭包。 您eval的文本可以引用函数的局部变量,并且在eval中甚至可以使用eval('var foo = …')创建新的局部变量eval('var foo = …')
  • 在函数内部使用new Function(…) ( 函数构造函数)时,它不会创建闭包。 (新函数不能引用外部函数的局部变量。)
  • JavaScript中的闭包就像保留所有局部变量的副本一样,就像它们退出函数时一样。
  • 最好考虑一下,总是总是只在函数的入口处创建一个闭包,并且将局部变量添加到该闭包中。
  • 每次调用带有闭包的函数时,都会保留一组新的局部变量(假设该函数内部包含一个函数声明,并且将返回对该内部函数的引用或以某种方式为其保留外部引用)。
  • 两个函数可能看起来像具有相同的源文本,但是由于它们的“隐藏”关闭而具有完全不同的行为。 我认为JavaScript代码实际上无法找出函数引用是否具有闭包。
  • 如果您尝试进行任何动态源代码修改(例如: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola')); ),则如果myFunction是闭包(当然,您甚至都不会想到在运行时进行源代码字符串替换,而是...)。
  • 可以在函数内的函数声明中获取函数声明……并且可以在多个级别上获得闭包。
  • 我认为通常闭包既是函数又是捕获的变量的术语。 请注意,我不在本文中使用该定义!
  • 我怀疑JavaScript中的闭包与功能性语言中的闭包不同。

链接

  • Douglas Crockford使用闭包为对象模拟了对象的私有属性和私有方法 。
  • 如果不小心,闭包将如何导致IE中的内存泄漏的绝佳解释。

谢谢

如果您刚刚学习了闭包(在这里或其他地方!),那么我对您可能会建议使本文更清晰的任何更改所产生的反馈意见感兴趣。 发送电子邮件至morrisjohns.com(morris_closure @)。 请注意,我不是JavaScript专家,也不是闭包专家。


莫里斯(Morris)的原始帖子可以在Internet存档中找到。


#3楼

闭包很像一个对象。 每当您调用函数时,它都会实例化。

JavaScript中闭包的范围是词法的,这意味着闭包所属函数中包含的所有内容都可以访问其中的任何变量。

如果您在闭包中包含一个变量,

  1. 给它分配var foo=1; 要么
  2. 只需写var foo;

如果内部函数(包含在另一个函数内部的函数)在不使用var定义其范围的情况下访问此类变量,则它将修改外部闭包中变量的内容。

闭包的寿命超过了产生它的函数的运行时间。 如果其他函数超出了定义它们的闭包/范围 (例如,作为返回值),则这些函数将继续引用该闭包

 function example(closure) { // define somevariable to live in the closure of example var somevariable = 'unchanged'; return { change_to: function(value) { somevariable = value; }, log: function(value) { console.log('somevariable of closure %s is: %s', closure, somevariable); } } } closure_one = example('one'); closure_two = example('two'); closure_one.log(); closure_two.log(); closure_one.change_to('some new value'); closure_one.log(); closure_two.log(); 

输出量

somevariable of closure one is: unchanged
somevariable of closure two is: unchanged
somevariable of closure one is: some new value
somevariable of closure two is: unchanged

#4楼

闭包很难解释,因为闭包用于使某些行为正常工作,每个人都希望它们能正常工作。 我发现解释它们的最佳方法(以及了解它们的方法)是想象没有它们的情况:

  var bind = function(x) { return function(y) { return x + y; }; } var plus5 = bind(5); console.log(plus5(3)); 

如果JavaScript 知道闭包,在这里会发生什么? 只需将最后一行的调用替换为其方法主体(基本上是函数调用所做的工作),您将获得:

console.log(x + 3);

现在, x的定义在哪里? 我们没有在当前范围内对其进行定义。 唯一的解决方案是让plus5 携带其范围(或更确切地说,其父级的范围)。 这样, x定义明确,并绑定到值5。


#5楼

每当您在另一个函数中看到function关键字时,内部函数就可以访问外部函数中的变量。

 function foo(x) { var tmp = 3; function bar(y) { console.log(x + y + (++tmp)); // will log 16 } bar(10); } foo(2); 

这将始终记录16,因为bar可以访问x它被定义为foo的参数),并且还可以从foo访问tmp

一个封闭。 一个函数不必为了被称为​​闭包而返回只需访问直接词法范围之外的变量即可创建闭包

 function foo(x) { var tmp = 3; return function (y) { console.log(x + y + (++tmp)); // will also log 16 } } var bar = foo(2); // bar is now a closure. bar(10); 

上面的函数也会记录16,因为bar仍然可以引用xtmp ,即使它不再直接在范围内。

但是,由于tmp仍然在bar的闭包内部徘徊,因此它也在增加。 每次调用bar时,它将增加。

关闭的最简单示例是:

 var a = 10; function test() { console.log(a); // will output 10 console.log(b); // will output 6 } var b = 6; test(); 

调用JavaScript函数时,将创建一个新的执行上下文。 与函数参数和父对象一起,此执行上下文还接收在其外部声明的所有变量(在上面的示例中,“ a”和“ b”)。

通过返回一个闭包函数列表或将它们设置为全局变量,可以创建多个闭包函数。 所有这些都将引用相同的 x和相同的tmp ,而不是自己制作副本。

在此,数字x是文字数字。 与JavaScript中的其他文字一样,当调用foo时,数字x 作为参数x 复制foo中。

另一方面,JavaScript在处理对象时总是使用引用。 如果说,您用一个对象调用了foo ,则它返回的闭包将引用该原始对象!

 function foo(x) { var tmp = 3; return function (y) { console.log(x + y + tmp); x.memb = x.memb ? x.memb + 1 : 1; console.log(x.memb); } } var age = new Number(2); var bar = foo(age); // bar is now a closure referencing age. bar(10); 

不出所料,每次调用bar(10)都会使x.memb递增。 可能不会想到的是, x只是与age变量引用相同的对象! 在两次致电barage.memb将为2! 此引用是HTML对象内存泄漏的基础。


#6楼

闭包是内部函数可以访问其外部函数中的变量的地方。 这可能是闭包最简单的单行解释。


#7楼

JavaScript中的函数不仅是对一组指令的引用(如C语言),而且还包括一个隐藏的数据结构,该结构由对其使用的所有非局部变量(捕获的变量)的引用组成。 这种两件式功能称为闭包。 JavaScript中的每个函数都可以视为闭包。

闭包是具有状态的功能。 从某种意义上说,它与“ this”类似,因为“ this”还提供了函数的状态,但function和“ this”是单独的对象(“ this”只是一个奇特的参数,并且是将其永久绑定到功能是创建一个闭包)。 虽然“ this”和函数始终是分开存在的,但不能将函数与其闭包分开,并且语言不提供访问捕获变量的方法。

因为词汇嵌套函数引用的所有这些外部变量实际上都是其词汇包围函数链中的局部变量(可以将全局变量假定为某个根函数的局部变量),并且每次执行函数都会创建一个新的实例。随之而来的是,函数每次执行返回(或以其他方式将其转移出去,例如将其注册为回调)嵌套函数时,都会创建一个新的闭包(具有其自身可能唯一的一组引用非局部变量,这些变量代表其执行)上下文)。

另外,必须理解,JavaScript中的局部变量不是在堆栈框架上创建的,而是在堆上创建的,并且仅在没有人引用它们时才销毁。 当函数返回时,对其局部变量的引用会递减,但如果在当前执行期间它们成为闭包的一部分并且仍由其词法嵌套的函数引用,则它们仍然可以为非空(仅当对这些嵌套函数被返回或以其他方式转移到某些外部代码)。

一个例子:

function foo (initValue) {//This variable is not destroyed when the foo function exits.//It is 'captured' by the two nested functions returned below.var value = initValue;//Note that the two returned functions are created right now.//If the foo function is called again, it will return//new functions referencing a different 'value' variable.return {getValue: function () { return value; },setValue: function (newValue) { value = newValue; }}
}function bar () {//foo sets its local variable 'value' to 5 and returns an object with//two functions still referencing that local variablevar obj = foo(5);//Extracting functions just to show that no 'this' is involved herevar getValue = obj.getValue;var setValue = obj.setValue;alert(getValue()); //Displays 5setValue(10);alert(getValue()); //Displays 10//At this point getValue and setValue functions are destroyed//(in reality they are destroyed at the next iteration of the garbage collector).//The local variable 'value' in the foo is no longer referenced by//anything and is destroyed too.
}bar();

#8楼

稻草人

我需要知道一个按钮被点击了多少次,并且每三次单击都会执行某项操作...

相当明显的解决方案

 // Declare counter outside event handler's scope var counter = 0; var element = document.getElementById('button'); element.addEventListener("click", function() { // Increment outside counter counter++; if (counter === 3) { // Do something every third time console.log("Third time's the charm!"); // Reset counter counter = 0; } }); 
 <button id="button">Click Me!</button> 

现在这将起作用,但是它通过添加变量来侵入外部范围,该变量的唯一目的是跟踪计数。 在某些情况下,这是可取的,因为您的外部应用程序可能需要访问此信息。 但是在这种情况下,我们仅更改每三次单击的行为,因此最好将此功能封装在事件处理程序中

考虑这个选项

 var element = document.getElementById('button'); element.addEventListener("click", (function() { // init the count to 0 var count = 0; return function(e) { // <- This function becomes the click handler count++; // and will retain access to the above `count` if (count === 3) { // Do something every third time console.log("Third time's the charm!"); //Reset counter count = 0; } }; })()); 
 <button id="button">Click Me!</button> 

注意这里的几件事。

在上面的示例中,我正在使用JavaScript的关闭行为。 此行为允许任何函数无限期地访问其创建范围。 为了实际应用此方法,我立即调用一个返回另一个函数的函数,并且由于我返回的函数可以访问内部count变量(由于上述闭包行为),因此会导致私有范围供结果使用功能...不是那么简单吗? 让我们稀释一下...

简单的单行闭包

//          _______________________Immediately invoked______________________
//         |                                                                |
//         |        Scope retained for use      ___Returned as the____      |
//         |       only by returned function   |    value of func     |     |
//         |             |            |        |                      |     |
//         v             v            v        v                      v     v
var func = (function() { var a = 'val'; return function() { alert(a); }; })();

返回函数以外的所有变量都可用于返回函数,但不能直接用于返回函数对象...

func();  // Alerts "val"
func.a;  // Undefined

得到它? 因此,在我们的主要示例中,count变量包含在闭包中,并且始终可供事件处理程序使用,因此它在单击之间保持其状态。

同样,此私有变量状态是完全可访问的,既可以读取也可以分配给其私有范围的变量。

你去了 您现在完全封装了此行为。

完整的博客文章 (包括jQuery注意事项)


#9楼

好吧,和一个6岁的孩子聊天,我可能会使用以下关联。

想象一下-您正在与整个房子里的弟弟和妹妹一起玩耍,并且带着玩具走来走去,并将其中一些带入哥哥的房间。 一段时间后,您的兄弟从学校返回并去了他的房间,他锁在房间里,所以现在您无法直接访问留在这里的玩具了。 但是你可以敲门,问你的兄弟那个玩具。 这称为玩具的闭合 ; 你的兄弟为你做了补偿,现在他已经进入了范围

与之相比,情况是一扇门被通风装置锁住,里面没有人(执行常规功能),然后发生局部火灾并烧毁了房间(垃圾收集器:D),然后建造了一个新房间,现在您可以离开了那里有另一个玩具(新功能实例),但是永远不会得到第一个房间实例中剩下的相同玩具。

对于高龄儿童,我会输入以下内容。 它不是完美的,但是会让您感觉到它是什么:

function playingInBrothersRoom (withToys) {// We closure toys which we played in the brother's room. When he come back and lock the door// your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him.var closureToys = withToys || [],returnToy, countIt, toy; // Just another closure helpers, for brother's inner use.var brotherGivesToyBack = function (toy) {// New request. There is not yet closureToys on brother's hand yet. Give him a time.returnToy = null;if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it.for ( countIt = closureToys.length; countIt; countIt--) {if (closureToys[countIt - 1] == toy) {returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!';break;}}returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.';}else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room.returnToy = 'Behold! ' + closureToys.join(', ') + '.';closureToys = [];}else {returnToy = 'Hey, lil shrimp, I gave you everything!';}console.log(returnToy);}return brotherGivesToyBack;
}
// You are playing in the house, including the brother's room.
var toys = ['teddybear', 'car', 'jumpingrope'],askBrotherForClosuredToy = playingInBrothersRoom(toys);// The door is locked, and the brother came from the school. You could not cheat and take it out directly.
console.log(askBrotherForClosuredToy.closureToys); // Undefined// But you could ask your brother politely, to give it back.
askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear
askBrotherForClosuredToy('ball'); // The brother would not be able to find it.
askBrotherForClosuredToy(); // The brother gives you all the rest
askBrotherForClosuredToy(); // Nothing left in there

如您所见,不管房间是否上锁,仍然可以通过兄弟访问留在房间里的玩具。 这是一个可玩的jsbin 。


#10楼

我只是将它们指向Mozilla Closures页面 。 这是我发现的关于闭包基础知识和实际用法的最佳,最简洁明了的解释 。 强烈建议任何学习JavaScript的人使用。

是的,我什至推荐给6岁的孩子使用-如果6岁的孩子正在学习闭包,那么可以随时理解本文提供的简洁明了的解释是合乎逻辑的。


#11楼

一个六岁孩子的答案(假设他知道什么是函数,什么是变量以及什么数据):

函数可以返回数据。 您可以从函数返回的一种数据是另一种函数。 返回该新函数时,创建该函数的函数中使用的所有变量和参数都不会消失。 而是,该父函数“关闭”。 换句话说,除了返回的功能外,什么都看不到它,也看不到它使用的变量。 该新函数具有特殊的功能,可以回顾创建它的函数内部并查看其中的数据。

function the_closure() {var x = 4;return function () {return x; // Here, we look back inside the_closure for the value of x}
}var myFn = the_closure();
myFn(); //=> 4

解释它的另一种非常简单的方法是在范围上:

每当您在较大范围内创建较小范围时,较小范围将始终能够看到较大范围中的内容。


#12楼

好吧,6岁的瓶盖粉丝。 您是否想听到最简单的闭包示例?

让我们想象下一个情况:驾驶员坐在汽车上。 那辆车在飞机上。 飞机在机场。 驾驶员进入汽车外部但进入飞机内部的功能(即使飞机离开机场)仍然是封闭的。 而已。 27岁时,请查看更详细的说明或以下示例。

这是将飞机上的故事转换为代码的方法。

 var plane = function(defaultAirport) { var lastAirportLeft = defaultAirport; var car = { driver: { startAccessPlaneInfo: function() { setInterval(function() { console.log("Last airport was " + lastAirportLeft); }, 2000); } } }; car.driver.startAccessPlaneInfo(); return { leaveTheAirport: function(airPortName) { lastAirportLeft = airPortName; } } }("Boryspil International Airport"); plane.leaveTheAirport("John F. Kennedy"); 

#13楼

通过GOOD / BAD比较,我倾向于学得更好。 我喜欢看到有人可能会遇到的工作代码,然后是非工作代码。 我整理了一个jsFiddle进行比较,并尝试将差异归结为我能想到的最简单的解释。

关闭正确:

console.log('CLOSURES DONE RIGHT');var arr = [];function createClosure(n) {return function () {return 'n = ' + n;}
}for (var index = 0; index < 10; index++) {arr[index] = createClosure(index);
}for (var index in arr) {console.log(arr[index]());
}
  • 在上面的代码中,循环的每次迭代都调用createClosure(n) 。 请注意,我将变量n命名为突出显示它是在新函数作用域中创建的变量,而不是与绑定到外部作用域的index相同的变量。

  • 这将创建一个新范围,并且n绑定到该范围; 这意味着我们有10个单独的范围,每个迭代一个。

  • createClosure(n)返回一个函数,该函数返回该范围内的n。

  • 在每个范围内, n绑定到调用createClosure(n)时具有的任何值,因此要返回的嵌套函数将始终返回调用createClosure(n)时具有的n的值。

关闭做错了:

console.log('CLOSURES DONE WRONG');function createClosureArray() {var badArr = [];for (var index = 0; index < 10; index++) {badArr[index] = function () {return 'n = ' + index;};}return badArr;
}var badArr = createClosureArray();for (var index in badArr) {console.log(badArr[index]());
}
  • 在上面的代码中,循环在createClosureArray()函数内移动,该函数现在仅返回完成的数组,乍一看似乎更直观。

  • 可能不明显的是,由于仅调用一次createClosureArray()才能为此函数创建一个作用域,而不是每次循环都创建一个作用域。

  • 在此函数中,定义了一个名为index的变量。 循环运行并将函数添加到返回index的数组中。 请注意, index是在createClosureArray函数中定义的,该函数只能被调用一次。

  • 由于createClosureArray()函数中只有一个作用域,因此index仅绑定到该范围内的一个值。 换句话说,每次循环更改index的值时,都会为该范围内引用它的所有内容更改它。

  • 添加到数组中的所有函数都从定义它的父作用域返回SAME index变量,而不是像第一个示例那样从10个不同作用域中返回10个不同变量。 最终结果是所有10个函数都从同一作用域返回相同的变量。

  • 循环结束并完成index修改后,最终值为10,因此,添加到数组的每个函数都将返回单个index变量的值,该值现在设置为10。

结果

正确关闭
n = 0
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9

关闭错误
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10


#14楼

闭包很简单:

以下简单示例涵盖了JavaScript闭包的所有要点。 *

这是一家生产可以加和乘的计算器的工厂:

function make_calculator() {var n = 0; // this calculator stores a single number nreturn {add: function(a) {n += a;return n;},multiply: function(a) {n *= a;return n;}};
}first_calculator = make_calculator();
second_calculator = make_calculator();first_calculator.add(3); // returns 3
second_calculator.add(400); // returns 400first_calculator.multiply(11); // returns 33
second_calculator.multiply(10); // returns 4000

关键点:每次对make_calculator调用make_calculator创建一个新的局部变量n ,在make_calculator返回很长时间之后,该变量的addmultiply函数仍可继续使用该局部变量。

如果您熟悉堆栈框架,这些计算器似乎很奇怪: make_calculator返回后,它们如何继续访问n 答案是想象JavaScript不使用“堆栈框架”,而是使用“堆框架”,该堆可以在使它们返回的函数调用之后持续存在。

addmultiply这样的内部函数被称为闭包 ,它们访问在外部函数**中声明的变量。

这几乎是闭包的全部内容。


*例如,它涵盖了另一个答案中 “封闭的傻瓜”一文中的所有要点,例6除外,该示例仅表明变量可以在声明之前使用,这是一个很好的事实,但与闭合完全无关。它还涵盖了接受的答案中的所有要点,除了以下几点:(1)函数将其参数复制到局部变量(命名的函数参数),以及(2)复制数字创建一个新数字,但复制对象引用给您另一个对同一对象的引用。这些也是很好知道的,但又与闭包完全无关。它也与该答案中的示例非常相似,但更简短,更抽象。它没有涵盖此答案或注释的要点,这是因为JavaScript使得很难将循环变量的当前值插入到您的内部函数中:“插入”步骤只能使用包含辅助函数的函数来完成您的内部函数,并在每次循环迭代时调用。(严格来说,内部函数访问变量的帮助器函数的副本,而不是插入任何内容。)再次,在创建闭包时非常有用,但不是闭包的一部分或工作方式的一部分。由于闭包在ML之类的功能语言中的工作方式不同,因此还有更多的困惑,其中变量绑定到值而不是存储空间,从而提供了源源不断的了解闭包的人(即“插入”方式),即对于JavaScript而言,这是完全不正确的,因为JavaScript总是将变量绑定到存储空间,而不是绑定到值。

**任何外部函数(如果有多个嵌套函数),甚至是在全局上下文中,正如该答案清楚指出的那样。


#15楼

我如何向六岁的孩子解释:

您知道大人如何拥有房屋,他们称之为房屋吗? 当妈妈有孩子时,孩子实际上并不拥有任何东西,对吗? 但是它的父母拥有一所房子,因此只要有人问孩子“你的房子在哪里?”,他/她就可以回答“那所房子!”,并指向其父母的房子。 “关闭”是孩子始终(即使在国外)也能够说自己拥有房屋的能力,即使这实际上是父母拥有房屋的能力。


#16楼

这是为了消除对其他一些答案中出现的闭包的几种(可能的)误解。

  • 闭包不仅在您返回内部函数时创建。 实际上,封闭函数根本不需要返回即可创建封闭函数。 您可以改为将内部函数分配给外部作用域中的变量,或将其作为参数传递给另一个函数,在该函数中可以立即或在以后的任何时间调用它。 因此,封闭函数的关闭很可能在调用封闭函数后立即创建因为只要在调用封闭函数之前或之后,任何内部函数都可以访问该封闭。
  • 闭包在其范围内未引用变量的旧值的副本。 变量本身是闭包的一部分,因此访问这些变量之一时看到的值是访问它时的最新值。 这就是为什么在循环内部创建内部函数会很棘手的原因,因为每个函数都可以访问相同的外部变量,而不是在创建或调用函数时获取变量的副本。
  • 闭包中的“变量”包括在函数内声明的任何命名函数。 它们还包括函数的参数。 闭包还可以访问其包含的闭包的变量,直到全局范围为止。
  • 闭包使用内存,但是它们不会导致内存泄漏,因为JavaScript本身会清理自己的未引用的循环结构。 当Internet Explorer无法断开引用闭包的DOM属性值的连接时,会创建涉及闭包的Internet Explorer内存泄漏,从而维护对可能的圆形结构的引用。

#17楼

作为一个六岁的孩子的父亲,他目前正在教幼儿(并且是一个相对新手,没有正规教育的编码,因此需要更正),我认为这堂课最好通过动手游戏来保持。 如果6岁的孩子准备好了解什么是封闭,那么他们已经足够大了,可以自己去尝试。 我建议将代码粘贴到jsfiddle.net中,进行一些解释,然后让它们单独编造一首独特的歌曲。 下面的解释性文字可能更适合10岁以下的儿童。

function sing(person) {var firstPart = "There was " + person + " who swallowed ";var fly = function() {var creature = "a fly";var result = "Perhaps she'll die";alert(firstPart + creature + "\n" + result);};var spider = function() {var creature = "a spider";var result = "that wiggled and jiggled and tickled inside her";alert(firstPart + creature + "\n" + result);};var bird = function() {var creature = "a bird";var result = "How absurd!";alert(firstPart + creature + "\n" + result);};var cat = function() {var creature = "a cat";var result = "Imagine That!";alert(firstPart + creature + "\n" + result);};fly();spider();bird();cat();
}var person="an old lady";sing(person);

使用说明

数据:数据是事实的集合。 它可以是数字,单词,量度,观察值,甚至只是事物的描述。 您不能触摸,闻到或尝尝它。 您可以写下来,说出来并听到。 您可以使用它在计算机上产生触摸气味和味道。 计算机可以使用代码使它变得有用。

代码:上面的所有文字都称为代码 。 它是用JavaScript编写的。

JAVASCRIPT:JavaScript是一种语言。 像英语或法语或中文都是语言。 计算机和其他电子处理器可以理解许多语言。 为了使JavaScript能够被计算机理解,它需要一个解释器。 想象一下,如果一位只会说俄语的老师来学校教您的课程。 当老师说“всесадятся”时,全班听不懂。 但是幸运的是,您的班上有一个俄罗斯学生,他告诉每个人这意味着“每个人都坐下”-你们都一样。 全班就像一台电脑,俄语学生是口译员。 对于JavaScript,最常见的解释器称为浏览器。

浏览器:当您通过计算机,平板电脑或手机上的Internet连接访问网站时,便使用了浏览器。 您可能知道的示例是Internet Explorer,Chrome,Firefox和Safari。 浏览器可以理解JavaScript并告诉计算机它需要做什么。 JavaScript指令称为函数。

功能:JavaScript中的函数就像一个工厂。 这可能是一个只有一台机器的小工厂。 或者它可能包含其他许多小工厂,每个工厂都有许多从事不同工作的机器。 在现实生活中的服装工厂中,可能会有成堆的布料和线筒进来,而T恤和牛仔裤就出来了。 我们的JavaScript工厂仅处理数据,无法缝制,钻孔或熔化金属。 在我们的JavaScript工厂中,数据进入并且数据出来。

所有这些数据听起来都有些无聊,但确实非常酷。 我们可能有一个告诉机器人晚餐的功能。 假设我邀请您和您的朋友来我家。 您最喜欢鸡腿,我喜欢香肠,您的朋友总是想要您想要的东西,而我的朋友不吃肉。

我没有时间去购物,因此该功能需要知道我们在冰箱中的存货才能做出决定。 每种食材的烹饪时间都不一样,我们希望机器人同时将所有食物加热。 我们需要向功能提供所需数据,功能可以与冰箱“对话”,并且功能可以控制机器人。

函数通常具有名称,括号和花括号。 像这样:

function cookMeal() {  /*  STUFF INSIDE THE FUNCTION  */  }

需要注意的是/*...*///由浏览器读取停止代码。

NAME:您可以随便使用任何单词都可以调用一个函数。 示例“ cookMeal”通常将两个单词连接在一起,并在第二个单词开头加一个大写字母-但这不是必需的。 它不能有空格,也不能单独是数字。

家长:“括号”或()是JavaScript函数工厂门上的信箱或街道上用于向工厂发送信息包的信箱。 有时可能会标记邮箱,例如 cookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime) ,在这种情况下,您知道必须提供哪些数据。

大括号:看起来像{} “括号”是我们工厂的有色窗户。 从工厂内部可以看到,但是从外部看不到。

上面的长代码示例

我们的代码以单词function开头,因此我们知道它是其中之一! 然后,函数的名称会唱歌 -这是我自己对函数含义的描述。 然后括号() 。 括号总是存在于函数中。 有时它们是空的,有时它们中有东西。这个(person)有一个词: (person) 。 在这之后有一个这样的括号{ 。 这标志着函数sing()的开始 。 它有一个标记sing()结束的伙伴,例如}

function sing(person) {  /* STUFF INSIDE THE FUNCTION */  }

因此,此功能可能与唱歌有关,并且可能需要有关某个人的一些数据。 它内部有指令来处理这些数据。

现在,在函数sing()之后 ,代码的结尾附近是该行

var person="an old lady";

变量:字母var代表“变量”。 变量就像一个信封。 在外面的信封上标有“人”字样。 它的内部包含一张纸条,上面有我们的功能所需的信息,一些字母和空格像一条绳子(称为绳子)连接在一起,构成一个短语,上面写着“一位老太太”。 我们的信封可能包含其他种类的东西,例如数字(称为整数),指令(称为函数),列表(称为数组 )。 因为此变量写在所有大括号{} ,并且因为当您位于大括号内时可以看到着色窗口,所以可以从代码的任何位置看到此变量。 我们称其为“全局变量”。

全局变量: 人员是全局变量,这意味着如果将其值从“老太太”更改为“年轻人”,则该将一直是年轻人,直到您决定再次更改它,并且其他任何功能代码可以看到这是一个年轻人。 按F12按钮或查看“选项”设置以打开浏览器的开发人员控制台,然后键入“ person”以查看该值是什么。 键入person="a young man"进行更改,然后再次键入“ person”以查看其已更改。

在此之后,我们有线

sing(person);

该行正在调用函数,就像在调用狗一样

“来 ,来吧,让的人 !”

当浏览器加载JavaScript代码并到达此行时,它将启动该功能。 我将这一行放在最后,以确保浏览器具有运行它所需的所有信息。

功能定义动作-主要功能是唱歌。 它包含一个名为firstPart的变量,该变量适用于歌唱每首歌的人的歌唱:“有“ +人+谁在吞咽”。 如果您在控制台中键入firstPart ,则不会得到答案,因为该变量已锁定在函数中-浏览器无法在花括号的着色窗口内看到。

关闭:关闭是在sing()函数内部的较小函数。 大工厂里面的小工厂。 它们每个都有自己的大括号,这意味着它们的变量无法从外部看到。 这就是为什么变量名( 生物结果 )可以在闭包中重复但值不同的原因。 如果在控制台窗口中键入这些变量名,则不会得到它的值,因为它被两层着色窗口隐藏了。

闭包都知道sing()函数名为firstPart的变量是什么,因为它们可以从其着色窗口中看到。

封闭之后,线路

fly();
spider();
bird();
cat();

sing()函数将按照给定的顺序调用这些函数。 然后,完成sing()函数的工作。


#18楼

dlaliberte第一点的示例:

闭包不仅在您返回内部函数时创建。 实际上,封闭函数根本不需要返回。 您可以改为将内部函数分配给外部作用域中的变量,或将其作为参数传递给可以立即使用的另一个函数。 因此,在调用封闭函数时,封闭函数的关闭可能已经存在,因为任何内部函数都可以在调用它后立即对其进行访问。

var i;
function foo(x) {var tmp = 3;i = function (y) {console.log(x + y + (++tmp));}
}
foo(2);
i(3);

#19楼

您可以向5岁的孩子解释关闭吗?*

我仍然认为Google的解释非常有效且简洁:

/*
*    When a function is defined in another function and it
*    has access to the outer function's context even after
*    the outer function returns.
*
* An important concept to learn in JavaScript.
*/function outerFunction(someNum) {var someString = 'Hey!';var content = document.getElementById('content');function innerFunction() {content.innerHTML = someNum + ': ' + someString;content = null; // Internet Explorer memory leak for DOM reference}innerFunction();
}outerFunction(1);​

* AC#问题


#20楼

我不明白为什么答案在这里这么复杂。

这是一个闭包:

var a = 42;function b() { return a; }

是。 您可能一天使用多次。

没有理由相信闭包是解决特定问题的复杂设计方法。 不, 从函数声明的位置(不运行)的角度来看 ,闭包只是使用来自更高范围的变量。

现在,它允许您执行的操作会更加壮观,请参阅其他答案。


#21楼

也许除了六岁的孩子中最早熟的孩子之外,还有其他一些例子,但有一些例子使我对JavaScript的关闭概念感到满意。

闭包是可以访问另一个函数的作用域(其变量和函数)的函数。 创建闭包的最简单方法是在函数中使用一个函数。 原因是在JavaScript中,函数始终可以访问其包含函数的作用域。

 function outerFunction() { var outerVar = "monkey"; function innerFunction() { alert(outerVar); } innerFunction(); } outerFunction(); 

警报:猴子

在上面的示例中,调用了externalFunction,依次调用了innerFunction。 请注意,innerFunction如何使用outerVar,可以通过正确警告outerVar的值来证明。

现在考虑以下几点:

 function outerFunction() { var outerVar = "monkey"; function innerFunction() { return outerVar; } return innerFunction; } var referenceToInnerFunction = outerFunction(); alert(referenceToInnerFunction()); 

警报:猴子

referenceToInnerFunction设置为outerFunction(),它仅返回对innerFunction的引用。 调用referenceToInnerFunction时,它将返回outerVar。 再次,如上所述,这证明了innerFunction可以访问outsideVar(outerFunction的变量)。 此外,有趣的是,即使在externalFunction完成执行后,它仍保留该访问权限。

在这里,事情变得非常有趣。 如果要摆脱outerFunction,例如将其设置为null,您可能会认为referenceToInnerFunction将失去对outerVar值的访问。 但这种情况并非如此。

 function outerFunction() { var outerVar = "monkey"; function innerFunction() { return outerVar; } return innerFunction; } var referenceToInnerFunction = outerFunction(); alert(referenceToInnerFunction()); outerFunction = null; alert(referenceToInnerFunction()); 

警报:猴子警报:猴子

但是怎么回事? 既然将outerFunction设置为null,referenceToInnerFunction仍如何知道outerVar的值?

referenceToInnerFunction仍然可以访问outerVar的值的原因是,当通过将innerFunction放置在outerFunction的内部来首次创建闭包时,innerFunction在其作用域链中添加了对outerFunction范围(其变量和函数)的引用。 这意味着innerFunction具有指向所有outerFunction变量(包括outerVar)的指针或引用。 因此,即使outerFunction完成执行,或者即使将其删除或设置为null,它范围内的变量(例如outerVar)也会在内存中停留,因为在internalFunction部分上对它们的出色引用referenceToInnerFunction。 为了从内存中真正释放outerVar和outerFunction的其余变量,您必须摆脱对它们的杰出引用,例如通过将referenceToInnerFunction也设置为null。

//

关于闭包要注意的另外两件事。 首先,闭包将始终可以访问其包含函数的最后一个值。

 function outerFunction() { var outerVar = "monkey"; function innerFunction() { alert(outerVar); } outerVar = "gorilla"; innerFunction(); } outerFunction(); 

警报:大猩猩

其次,在创建闭包时,它会保留对其所有封闭函数的变量和函数的引用; 它没有选择。 但是,因此,应该谨慎使用闭包,或者至少要谨慎使用闭包,因为它们可能占用大量内存; 在包含函数完成执行后很长一段时间内,许多变量可以保留在内存中。


#22楼

即使父母走了,孩子们也将永远记住与父母分享的秘密。 这就是函数的闭包。

JavaScript函数的秘密是私有变量

var parent = function() {var name = "Mary"; // secret
}

每次调用它时,都会创建局部变量“ name”,并给定名称“ Mary”。 并且每次函数退出时,变量都会丢失,并且名称也将被忘记。

您可能会猜到,因为每次调用函数时都会重新创建变量,并且没人会知道它们,所以必须在一个秘密的地方存储它们。 可以将其称为“密室”或“ 堆栈”或“ 本地范围”,但这并不重要。 我们知道它们在那里,藏在内存中。

但是,在JavaScript中,有一个非常特殊的东西,即在其他函数内部创建的函数也可以知道其父级的局部变量,并在它们存在之前一直保留它们。

var parent = function() {var name = "Mary";var child = function(childName) {// I can also see that "name" is "Mary"}
}

因此,只要我们处于父函数中,它就可以创建一个或多个子函数,这些子函数确实共享秘密位置中的秘密变量。

但是,令人遗憾的是,如果子项也是其父函数的私有变量,则它也将在父项结束时死亡,而秘密也将随之消失。

为了生存,孩子必须在为时已晚之前离开

var parent = function() {var name = "Mary";var child = function(childName) {return "My name is " + childName  +", child of " + name; }return child; // child leaves the parent ->
}
var child = parent(); // < - and here it is outside

而现在,即使玛丽“不再跑步”,她的记忆也不会丢失,她的孩子将永远记住她的名字和他们在一起时分享的其他秘密。

因此,如果您称孩子“爱丽丝”,她会回应

child("Alice") => "My name is Alice, child of Mary"

这就是全部。


#23楼

Closures的作者很好地解释了闭包,解释了我们为什么需要它们的原因,还解释了LexicalEnvironment,这对于理解闭包是必需的。
这是摘要:

如果访问变量但不是局部变量怎么办? 像这儿:

在这种情况下,解释器在外部LexicalEnvironment对象中找到变量。

该过程包括两个步骤:

  1. 首先,创建函数f时,不会在空白处创建它。 当前有一个LexicalEnvironment对象。 在上述情况下,它是一个窗口(函数创建时未定义a)。

创建函数时,它会获得一个名为[[Scope]]的隐藏属性,该属性引用当前的LexicalEnvironment。

如果读取了变量,但在任何地方都找不到,则会生成错误。

嵌套函数

函数可以彼此嵌套,形成LexicalEnvironments链,也可以称为作用域链。

因此,函数g可以访问g,a和f。

关闭

外部函数完成后,嵌套函数可能会继续存在:

标记词法环境:

如我们所见, this.say是用户对象中的一个属性,因此它在用户完成后继续存在。

并且,如果您还记得,当创建this.say时,它(作为每个函数)将获得内部this.say.[[Scope]]对当前LexicalEnvironment的引用。 因此,当前User执行的LexicalEnvironment保留在内存中。 User的所有变量也都是其属性,因此也要小心保留它们,而不是像平常一样。

关键是要确保内部函数将来要访问外部变量,它能够这样做。

总结一下:

  1. 内部函数保留对外部LexicalEnvironment的引用。
  2. 即使外部函数完成,内部函数也可以随时从中访问变量。
  3. 浏览器将LexicalEnvironment及其所有属性(变量)保留在内存中,直到有内部函数引用它为止。

这称为关闭。


#24楼

不久前,我写了一篇博客文章解释了闭包。 这就是我说的关于闭包的原因因为您想要一个闭包。

闭包是一种使函数具有永久性私有变量的方法,也就是说,只有一个函数才知道的变量,它可以在其中跟踪以前运行时的信息。

从这种意义上讲,它们使函数的行为有点像具有私有属性的对象。

全文:

那么这些闭包到底是什么呢?


#25楼

JavaScript函数可以访问它们:

  1. 争论
  2. 局部变量(即它们的局部变量和局部函数)
  3. 环境,包括:
    • 全球,包括DOM
    • 外部功能中的任何东西

如果函数访问其环境,则该函数为闭包。

注意,外部函数不是必需的,尽管它们确实提供了好处,我在这里不讨论。 通过访问其环境中的数据,闭包可以使该数据保持活动状态。 在外部/内部函数的子情况下,外部函数可以创建本地数据并最终退出,但是,如果任何内部函数在外部函数退出后仍然存在,则内部函数将保留外部函数的本地数据活。

使用全局环境的闭包示例:

想象一下,堆栈溢出投票和投票下降按钮事件是作为闭包,votUp_click和voteDown_click实现的,它们可以访问全局定义的外部变量isVotedUp和isVotedDown。 (为简单起见,我指的是StackOverflow的“问题投票”按钮,而不是“答案投票”按钮的数组。)

当用户单击VoteUp按钮时,voteUp_click函数将检查isVotedDown == true,以确定是投票还是只取消反对票。 函数votUp_click是一个闭包,因为它正在访问其环境。

var isVotedUp = false;
var isVotedDown = false;function voteUp_click() {if (isVotedUp)return;else if (isVotedDown)SetDownVote(false);elseSetUpVote(true);
}function voteDown_click() {if (isVotedDown)return;else if (isVotedUp)SetUpVote(false);elseSetDownVote(true);
}function SetUpVote(status) {isVotedUp = status;// Do some CSS stuff to Vote-Up button
}function SetDownVote(status) {isVotedDown = status;// Do some CSS stuff to Vote-Down button
}

所有这四个功能都是闭包,因为它们都访问环境。


#26楼

前言:当问题是:

就像老阿尔伯特所说:“如果您不能向六岁的孩子解释它,那么您自己真的不理解。”好吧,我试图向一位27岁的朋友解释JS的关闭,但完全失败了。

有人可以认为我6岁并对这个主题感到奇怪吗?

我敢肯定,我是唯一尝试从字面上回答最初问题的人之一。 从那时起,这个问题已经改变了几次,所以我的答案现在似乎变得非常愚蠢和不合适。 希望这个故事的总体思路对某些人仍然很有趣。


在解释困难的概念时,我非常喜欢类比和隐喻,所以让我尝试一个故事。

很久以前:

有一位公主

function princess() {

她生活在一个充满冒险的奇妙世界中。 她遇到了白马王子,骑着独角兽环游世界,与巨龙作战,遇到了会说话的动物,以及许多其他奇幻的事物。

    var adventures = [];function princeCharming() { /* ... */ }var unicorn = { /* ... */ },dragons = [ /* ... */ ],squirrel = "Hello!";/* ... */

但是她总是必须回到繁琐的琐事和大人的世界。

    return {

而且她经常会告诉他们她作为公主的最新奇妙冒险。

        story: function() {return adventures[adventures.length - 1];}};
}

但是他们只会看到一个小女孩...

var littleGirl = princess();

...讲述魔术和幻想的故事

littleGirl.story();

即使大人们知道真正的公主,他们也永远不会相信独角兽或龙,因为他们永远看不到它们。 大人们说,它们只存在于小女孩的想像力之内。

但是我们知道真实的事实; 那个里面有公主的小女孩...

...真的是一个里面有一个小女孩的公主。


#27楼

您正在睡觉,请Dan。 您告诉Dan带一个XBox控制器。

丹邀请保罗。 丹请保罗带一名管制员。 有多少控制者参加了聚会?

function sleepOver(howManyControllersToBring) {var numberOfDansControllers = howManyControllersToBring;return function danInvitedPaul(numberOfPaulsControllers) {var totalControllers = numberOfDansControllers + numberOfPaulsControllers;return totalControllers;}
}var howManyControllersToBring = 1;var inviteDan = sleepOver(howManyControllersToBring);// The only reason Paul was invited is because Dan was invited.
// So we set Paul's invitation = Dan's invitation.var danInvitedPaul = inviteDan(howManyControllersToBring);alert("There were " + danInvitedPaul + " controllers brought to the party.");

#28楼

我整理了一个交互式JavaScript教程,以解释闭包是如何工作的。 什么是封闭?

这是示例之一:

var create = function (x) {var f = function () {return x; // We can refer to x here!};return f;
};
// 'create' takes one argument, creates a functionvar g = create(42);
// g is a function that takes no arguments nowvar y = g();
// y is 42 here

#29楼

关于关闭的维基百科 :

在计算机科学中,闭包是一个函数,以及对该函数的非本地名称(自由变量)的引用环境。

从技术上讲,在JavaScript中 , 每个函数都是一个闭包 。 它始终可以访问在周围范围内定义的变量。

由于JavaScript中的作用域定义构造是一个函数 ,而不是许多其他语言中的代码块,因此JavaScript中的闭包通常指的是一个函数,函数使用已执行的周围函数中定义的非局部变量

闭包通常用于创建带有一些隐藏的私有数据的函数(但并非总是如此)。

var db = (function() {// Create a hidden object, which will hold the data// it's inaccessible from the outside.var data = {};// Make a function, which will provide some access to the data.return function(key, val) {if (val === undefined) { return data[key] } // Getelse { return data[key] = val } // Set}// We are calling the anonymous surrounding function,// returning the above inner function, which is a closure.
})();db('x')    // -> undefined
db('x', 1) // Set x to 1
db('x')    // -> 1
// It's impossible to access the data object itself.
// We are able to get or set individual it.

EMS

上面的示例使用一个匿名函数,该函数执行一次。 但这不是必须的。 可以命名它(例如mkdb )并在以后执行,每次调用它都会生成一个数据库函数。 每个生成的函数都有其自己的隐藏数据库对象。 闭包的另一个用法示例是当我们不返回一个函数,而是一个对象,该对象包含出于不同目的的多个函数,这些函数中的每个函数都可以访问相同的数据。


#30楼

认真对待这个问题,我们应该找出一个典型的6岁孩子在认知上有什么能力,尽管可以肯定的是,对JavaScript感兴趣的人并不是那么典型。

关于儿童发展:5至7岁,它说:

您的孩子将能够遵循两步指导。 例如,如果您对孩子说“去厨房给我一个垃圾袋”,他们将能够记住该方向。

我们可以使用该示例来说明闭包,如下所示:

厨房是一个有局部变量的封闭trashBags ,称为trashBags 。 厨房内部有一个名为getTrashBag的函数,该函数获取一个垃圾袋并返回。

我们可以这样在JavaScript中进行编码:

 function makeKitchen() { var trashBags = ['A', 'B', 'C']; // only 3 at first return { getTrashBag: function() { return trashBags.pop(); } }; } var kitchen = makeKitchen(); console.log(kitchen.getTrashBag()); // returns trash bag C console.log(kitchen.getTrashBag()); // returns trash bag B console.log(kitchen.getTrashBag()); // returns trash bag A 

进一步说明了为什么闭包有趣的原因:

  • 每次调用makeKitchen() ,都会使用其自己的单独的trashBags创建一个新的闭包。
  • trashBags变量是每个厨房内部的局部变量,外部不能访问,但是getTrashBag属性的内部函数可以访问该getTrashBag
  • 每个函数调用都会创建一个闭包,但是除非可以从闭包外部调用可以访问闭包内部的内部函数,否则无需保持闭包。 在这里使用getTrashBag函数返回对象。

JavaScript闭包如何工作?相关推荐

  1. [转]Javascript 闭包

    [转]Javascript 闭包 简介 Closure 所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 闭 ...

  2. 理解 JavaScript 闭包

    简介 Closure 所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 闭包是 ECMAScript (Ja ...

  3. javascript闭包_通过邮寄包裹解释JavaScript闭包

    javascript闭包 by Kevin Kononenko 凯文·科诺年科(Kevin Kononenko) 通过邮寄包裹解释JavaScript闭包 (JavaScript Closures E ...

  4. 好程序员前端教程之JavaScript闭包和匿名函数的关系详解...

    好程序员前端教程之JavaScript闭包和匿名函数的关系详解 本文讲的是关于JavaScript闭包和匿名函数两者之间的关系,从匿名函数概念到立即执行函数,最后到闭包.下面一起来看看文章分析,希望你 ...

  5. 理解 JavaScript 闭包{转载}

    本文转载自:http://www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html 要成为高级 JavaScript 程序 ...

  6. 【译】理解JavaScript闭包——新手指南

    闭包是JavaScript中一个基本的概念,每个JavaScript开发者都应该知道和理解的.然而,很多新手JavaScript开发者对这个概念还是很困惑的. 正确理解闭包可以帮助你写出更好.更高效. ...

  7. Javascript闭包简单理解

    Javascript闭包简单理解 原文:Javascript闭包简单理解 提到闭包,想必大家都早有耳闻,下面说下我的简单理解. 说实话平时工作中实际手动写闭包的场景并不多,但是项目中用到的第三方框架和 ...

  8. 好程序员技术分析JavaScript闭包特性详解

    为什么80%的码农都做不了架构师?>>>    好程序员技术分析JavaScript闭包特性详解,今天来总结一下js闭包的那些事,以及遇到的坑和解决方法,希望对你有所帮助. 是的,没 ...

  9. JavaScript 是如何工作的:模块的构建以及对应的打包工具

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 20 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

最新文章

  1. background 旋转_第4章 旋转的圆弧(《Python趣味创意编程》教学视频)
  2. JAGUARSDN1网络的开机自动启动 禁用,WIFI模块的启动关闭控制
  3. 跟我学Telerik公司的RadControls控件(四)
  4. Linux下使用curl
  5. 在Intel处理器上,修改mitigations参数使Linux系统运行得更快
  6. 搜狗快速排名软件到底哪家更强大?
  7. c语言以顺序结构存储的二叉树的非递归遍历,一种二叉树非递归遍历算法的C语言实现...
  8. DedeCMS总是提示验证码不正确的解决方法
  9. 自动化测试处理银行密码输入框
  10. IOS开发-苹果开发者中心 提示 edit phone number
  11. 小马哥-----高仿苹果6 plus刷机 详细拆机主板图与开机界面图与移植触摸屏版中文rec 界面 多图展示
  12. MySQL: Incorrect string value: '\xF0\xA4\xBD\x82'分析解决
  13. FPGA实现信号n倍插值(内插0)
  14. Codeforces A. XXXXX (数学 / 双指针) (Round #649 Div.2)
  15. Pytorch使用autograd.Function自定义拓展神经网络
  16. String 判断纯数字 正则式
  17. 《深度学习之TensorFlow》reading notes(3)—— MNIST手写数字识别之二
  18. html转换成移动端h5,HTML5移动端手机网站基本模板
  19. java lru_LRU的理解与Java实现
  20. 安装Python包,网络不可达解决方法

热门文章

  1. 第五周项目三-时间类(2)
  2. 我的世界java刷怪数量_我的世界Minecraft源码分析(1):刷怪逻辑
  3. python数据插值_【Python】【数据分析】缺失值处理——插值
  4. (0093)iOS开发自己账号跑真机报错:conflicting provisioning
  5. Windows服务器SYSTEM权限Webshell无法添加3389账户情况突破总结
  6. C++ delete 和 delete []的区别
  7. 如何用纯 CSS 创作一个单元素抛盒子的 loader
  8. 正负样本比率失衡SMOTE
  9. Could not find the main class: org.apache.catalina.startup.Boostrap. Program will exit.
  10. Git reset , revert, checkout的区别和联系