怎么JavaScript封锁的工作?
-
02-07-2019 - |
题
你怎么解释JavaScript封锁的人有知识的概念,他们由(例如职能、变量等),但不理解封闭自己?
我已经看到 该方案的例子 鉴于在维基百科,但不幸的是,它没有帮助。
解决方案
JavaScript封锁对于初学者
提交莫里斯上星期二,2006-02-21 10:19.社区的编辑。
封锁是不是魔法
该网页介绍了关闭这样一个程序员可以了解他们使用的工作JavaScript code.这不是大师或功能的程序员。
封锁是 不难 要理解一旦核心概念是grokked.但是,它们不可能理解通过阅读任何理论或学业成绩为导向的解释!
本文的目的是为程序的一些编程经验的主流语言,以及谁可以阅读以下JavaScript功能:
function sayHello(name) {
var text = 'Hello ' + name;
var say = function() { console.log(text); }
say();
}
sayHello('Joe');
两个简短的摘要
当一个功能(
foo
)宣布的其他职能(酒吧和baz),家庭中的地方创建的变量在foo
是 未被破坏 当功能退出。变量只是成为看不见外面的世界。foo
因此可以狡猾的回报的职能bar
和baz
, 和他们可以继续读、写和相互通信,通过这个封闭的家庭的变量("封闭"),没有其他人可以插手,甚至没有人谁的来电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会认为该函作为返回的一个指向一个功能,而是变量 say
和 say2
每一个指向一个功能。
还有一个重要区别之间C指来函数和JavaScript参考到一个功能。在JavaScript,你能想到的一个功能的基准变为具有既指一种功能 以及 作为一个隐藏的指针指向一个封闭。
上述代码中有一关闭,因为匿名的功能 function() { console.log(text); }
声明 内部 另一个功能, sayHello2()
在这个例子。在JavaScript,如果你使用 function
关键字里面的另一个功能,创建一个封闭。
在C和大多数其他共同的语言, 后 一个功能返回,所有的地方变量没有再访问,因为栈-框架是摧毁。
在JavaScript,如果声明内的功能的另一个功能,那么这地方变量外的功能可以保持访问之后返回。这证明了上述,因为我们呼功能 say2()
之后我们回来 sayHello2()
.注意到代码,我们呼引用的变量 text
, ,这是一个 当地变量 的功能 sayHello2()
.
function() { console.log(text); } // Output of say2.toString();
看的出 say2.toString()
, 我们可以看到,代码指的是变量 text
.匿名的功能可以参考 text
其拥有的价值 'Hello Bob'
因为当地的变量 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()
再次,然后,一个新的封闭(叠框架!) 被创建。旧 gLogNumber
, gIncreaseNumber
, gSetNumber
变量的复盖 新的 功能的新的关闭。(JavaScript,当你宣布一个功能内部的另一个功能、内部函(s)/们再次重建 每 时间之外的功能。)
例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
(这是 result
, i
, list
和 item
).当匿名的功能是所谓的在线 fnlist[j]()
;它们都使用相同的单一封闭,并且他们使用的当前值 i
和 item
在一个封闭(这里 i
有价值的 3
因为循环已经完成, item
有价值的 'item2'
).注意,我们编制索引,从0因此 item
有价值的 item2
.和我++将增加 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);
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;
摘要
如果一切似乎都完全不清楚,然后做的最好的事情是玩具的例子。阅读的解释是更难于理解的例子。我的解释的封锁和栈的框架,等等。不是技术上是正确的—他们总简化旨在帮助理解。一旦基本思想是grokked,你可以挑选最多的详情。
最后一点:
- 只要你使用
function
里面的另一个功能,一个封闭使用。 - 只要你使用
eval()
内部的功能、一个封闭使用。文本eval
可以参考的地方变量的功能,并在eval
你甚至可以创建新的局部变量通过使用eval('var foo = …')
- 当你使用
new Function(…)
(的 功能的构造)内部一个功能,它并不创建一个封闭。(新的功能不能参考的地方变量外的功能。) - 封闭在JavaScript是喜欢保持一个复制的所有地方的变量,只是因为他们是当一个功能退出。
- 这可能是最好认为,一个封闭总是创造一个入口,一个功能,而地方变量加以关闭。
- 一个新设的当地变量保持每次一个功能用一种封闭是被称为(定,功能包含一个功能宣言》内部,和一个参考,内部功能是返回或外部基准是保持它在一些方式)。
- 两种功能可能看起来像他们有相同的来源的文字,但具有完全不同的行为,因为他们的"隐藏"封闭。我不认为JavaScript code实际上可以找到,如果一个功能的基准有一个封闭或没有。
- 如果你们试图要做的任何动态的源代码的修改(例如:
myFunction = Function(myFunction.toString().replace(/Hello/,'Hola'));
),它不会工作如果myFunction
是一个封闭(当然,你不会想这样做的源代码串替代运行时,但是...). - 它是可以得到功能的声明在功能的声明内的功能...你可以得到的关闭在一个以上的水平。
- 我认为通常一个封闭是一个术语用于两者的功能沿用的变量抓获。注意,我做不使用这一定义在本条!
- 我怀疑,关闭在JavaScript不同于那些通常发现在的功能的语言。
链接
- 道格拉斯Crockford的模拟 私人的属性和私人的方法 为对象,使用封锁。
- 一个伟大的解释如何可以关闭 导致泄漏内存在即 如果你不小心。
感谢
如果你有 只是 了解到关闭(这里还是在其他地方!), 然后我感兴趣的任何反馈意见,从你的任何改变可能表明,可能使本条更加清晰。发送电子邮件至morrisjohns.com (morris_closure@).请注意,我不是大师JavaScript—也不对关闭。
原来的职位由莫里斯,可现在 互联网上的档案.
其他提示
每当你在另一个函数中看到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
仍然可以引用 x
和 tmp
,即使它不再直接在里面范围。
但是,由于 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
的数字被复制到 foo
作为其参数<代码> X 代码>
另一方面,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
变量相同的对象!在对 bar
进行几次调用后, age.memb
将为2!此引用是HTML对象内存泄漏的基础。
前言:这个答案是在问题出现时写的:
像老阿尔伯特所说的那样:“如果你无法向六岁的孩子解释它,你自己真的不明白。”#8221;好吧,我试图向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();
尽管成年人知道真正的公主,但他们永远不会相信独角兽或龙,因为他们永远看不到它们。成年人说他们只存在于小女孩的想象中。
但我们知道真相;那个公主里面的小女孩......
......真是个公主,里面有一个小女孩。
如果问题严重的是,我们应该找出什么样的一个典型的6岁是能够认知,虽然无可否认,一个是谁感兴趣的JavaScript不是那么典型。
上 儿童发展:5到7年 它说:
你的孩子将能够按照两步骤的方向。例如,如果你说你的孩子,"去厨房帮我一个垃圾袋",他们将能够记住这个方向前进。
我们可以用这个例子来说明封锁如下:
厨房是一个关闭,有一个地方变量,叫
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
功能不在这里。
稻草人
我需要知道点击按钮的次数,并且每按三次点击就做一次......
相当明显的解决方案
// 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;
}
};
})());
// _______________________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); }; })();
请注意这里的一些事情。
在上面的例子中,我使用的是JavaScript的闭包行为。 此行为允许任何函数无限期地访问创建它的作用域。为了实际应用它,我立即调用一个返回另一个函数的函数,因为我返回的函数有访问内部计数变量(由于上面解释的闭包行为),这导致生成的函数使用私有范围...不是那么简单?让我们淡化它......
简单的单行关闭
func(); // Alerts "val"
func.a; // Undefined
返回函数之外的所有变量都可用于返回的函数,但它们不能直接用于返回的函数对象...
<*>得到它?因此,在我们的主要示例中,count变量包含在闭包中,并且始终可用于事件处理程序,因此它从单击到单击保持其状态。
此外,对于读数和分配给其私有范围变量,此私有变量状态完全可访问。
你走了;你现在完全封装了这种行为。
完整博客帖子 (包括jQuery)考虑)
闭包很难解释,因为它们被用来做一些每个人都直觉期望工作的行为。我发现解释它们的最佳方式(以及我了解他们所做的事情的方式)是想象没有它们的情况:
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。
这是试图澄清若干(可能的)的误解关于关闭中出现的一些其他的答案。
- 一个封闭的不仅是创建当你返回内的功能。 事实上,封闭功能 不需要返回在所有 为了对其封闭。你可能,而不是分配你内心的函数变量,在外范围,或通过它作为一个参数的另一个功能,它可以被称为立即或任何时间以后。因此,关闭在封闭功能,可能是创建 尽快封闭功能是所谓 因为任何内部的功能已经访问封闭的,只要内部功能是所谓的之前或之后封闭功能返回。
- 一封不参照的副本 老值 变量在其范围。 变量自身的一部分封闭,因此值看到,在访问之一,这些变量是最新的价值时进行访问。这就是为什么内的功能的内部创建的循环,可能是棘手的,因为每个人都有接到相同的外部变数而不是抓住一个复制的变量,当时的功能是建立或调用。
- 在"变量",在封闭包括任何指定的功能 声明的内的功能。它们还包括参数的函数。一个封闭的还有访问它的含有封闭的变量,所有的方式在全球范围。
- 封锁使用的存储器,但它们不会导致存储器泄漏 由于JavaScript本身可以清理它自己的圆形结构,都没有引用。Internet Explorer存储器泄漏涉及封锁是创建时,它无法断开DOM属性值得参考的封锁,从而维持的参考可能的圆形结构。
让我们想象下一个情况:司机坐在车里。那辆车在飞机内。飞机在机场。驾驶员能够接近车外的东西,但是在飞机内部,即使飞机离开机场,也是一种封闭。而已。当您转到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");
闭包就像一个对象。无论何时调用函数,它都会被实例化。
JavaScript中闭包的范围是词法,这意味着闭包所属的函数中包含的所有内容都可以访问任何变量。它
如果你
,变量包含在闭包中- 使用
var foo = 1;
或 进行分配
- 只需编写
var foo;
醇>
如果内部函数(包含在另一个函数中的函数)访问这样的变量而没有在var自己的范围内定义它,它会修改外部闭包中变量的内容。
闭包比产生它的函数的运行时更长。如果其他函数使它超出定义它们的 closure / scope (例如作为返回值),那些将继续引用 closure 。
实施例
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
我在一段时间后写了一篇博文,解释了闭包。这就是我所说的关于为什么你想要的闭包的内容。
闭包是一种让函数出现的方法 拥有持久性私有变量 - 也就是说,变量只有一个 功能知道,它可以在哪里 跟踪以前的信息 它已经运行了。
从这个意义上说,他们让一个函数看起来有点像具有私有属性的对象。
全文:
关闭很简单:
以下简单示例涵盖了JavaScript闭包的所有要点。 * &NBSP;
这是一家生产可以增加和增加的计算器的工厂:
function make_calculator() {
var n = 0; // this calculator stores a single number n
return {
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 400
first_calculator.multiply(11); // returns 33
second_calculator.multiply(10); // returns 4000
关键点:每次调用 make_calculator
都会创建一个新的局部变量 n
,该计算器的继续可以使用在
和 make_calculator
返回后很长时间添加乘以
函数。
如果您熟悉堆栈帧,这些计算器似乎很奇怪:在 make_calculator
返回后,他们如何继续访问 n
?答案是假设JavaScript不使用“堆栈帧”,而是使用“堆帧”,它可以在使它们返回的函数调用之后持久存在。
内部函数,如 add
和 multiply
,它们访问外部函数 ** 中声明的变量,称为闭包
这几乎就是关闭所有内容。
结果
* 例如,它涵盖了“傻瓜的封闭”中的所有要点。在另一个答案中给出的文章,除了示例6,它只是表明变量可以在声明之前使用,这是一个很好的事实要知道但与封闭完全无关。它还涵盖了接受的答案中的所有要点,除了函数将其参数复制到局部变量的要点(1) (已命名的函数参数),以及(2)复制数字会创建一个新数字,但复制对象引用会为您提供对同一对象的另一个引用。这些也很好知道但又与封闭完全无关。它也与此答案中的示例非常相似,但有点简短且不那么抽象。它没有涵盖此答案或此评论,这使JavaScript很难插入循环变量的当前值进入你的内在功能:“插入” step只能使用包含内部函数的辅助函数来完成,并在每次循环迭代时调用。 (严格地说,内部函数访问辅助函数的变量副本,而不是插入任何东西。)同样,在创建闭包时非常有用,但不是闭包的一部分或它是如何工作的。由于闭包在ML等函数式语言中的工作方式不同而存在额外的混淆,其中变量绑定到值而不是存储空间,从而提供了一种以某种方式理解闭包的人(即“插入”方式)的持续流。对JavaScript来说根本就是不正确的,其中变量总是绑定到存储空间,而永远不会绑定到值。功能
** 任何外部函数,如果有多个是嵌套的,甚至是在全局上下文中,如这个答案明确指出。
我如何向一个六岁的孩子解释:
你知道成年人如何拥有房子,他们称之为家吗?当一个妈妈有一个孩子时,孩子并没有真正拥有任何东西,对吗?但是它的父母拥有一所房子,所以每当有人问孩子“你的家在哪里?”时,他/她就可以回答“那个房子!”,并指向其父母的房子。 “闭合”是孩子总是(即使在国外)能够说它有家的能力,即使它真的是父母拥有房子。
你能解释一下关闭吗?一个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);
* C#问题
我倾向于更好地学习,通过良好的/不良的比较。我喜欢看到工作的代码随后的非工作的代码,有人可能会遇到。我放在一起 一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)
援引这样的嵌套功能的返回将总回报的价值n
它不得不当createClosure(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
, ,它改变了这一切引用它在这一范围。所有的职能添加到数组返回相同的
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
在计算机科学中,闭包是一个函数以及该函数的非局部名称(自由变量)的引用环境。
从技术上讲,在 JavaScript 中,每个功能都是一个闭包。它始终可以访问在周围范围内定义的变量。
由于JavaScript中的范围定义构造是一个函数,而不是像许多其他语言一样的代码块,我们通常在JavaScript中使用 closure 的意思是一个函数,用于在已经执行的周围函数中定义的非局部变量。
闭包通常用于创建具有一些隐藏私有数据的函数(但情况并非总是如此)。
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] } // Get
else { 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
)并在以后执行,每次调用时都会生成一个数据库函数。每个生成的函数都有自己的隐藏数据库对象。闭包的另一个用法示例是当我们不返回一个函数,而是一个包含多个函数用于不同目的的对象时,每个函数都可以访问相同的数据。
我整理了一个交互式JavaScript教程来解释闭包的工作原理。 什么是关闭?
以下是其中一个例子:
var create = function (x) {
var f = function () {
return x; // We can refer to x here!
};
return f;
};
// 'create' takes one argument, creates a function
var g = create(42);
// g is a function that takes no arguments now
var y = g();
// y is 42 here
孩子们将永远记住他们与父母分享的秘密,即使他们的父母也是如此 不见了。这就是闭包的功能。
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"
这就是要说的。
我不明白为什么答案在这里很复杂。
这是一个闭包:
var a = 42;
function b() { return a; }
是。你可能每天都要多次使用它。
结果
没有理由相信闭包是解决特定问题的复杂设计攻击。不,闭包仅仅是从声明函数(未运行)的角度使用来自更高范围的变量。
现在允许你能做什么可以更加壮观,请看其他答案。
dlaliberte的第一点示例:
不仅在返回内部函数时创建闭包。实际上,封闭功能根本不需要返回。您可以将内部函数分配给外部作用域中的变量,或者将其作为参数传递给另一个可以立即使用的函数。因此,封闭函数的闭包可能在调用封闭函数时已经存在,因为任何内部函数在调用它时都可以访问它。
var i;
function foo(x) {
var tmp = 3;
i = function (y) {
console.log(x + y + (++tmp));
}
}
foo(2);
i(3);
闭包是内部函数可以访问其外部函数中的变量的地方。这可能是闭包最简单的一行解释。
我知道已经有很多解决方案,但我想这个小而简单的脚本可以用来演示这个概念:
// makeSequencer will return a "sequencer" function
var makeSequencer = function() {
var _count = 0; // not accessible outside this function
var 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
你正在睡觉,你邀请丹。 你告诉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.");
JavaScript功能访问他们:
- 参数
- 当地(那是他们的地方的变量和地方的职能)
- 环境,其中包括:
- globals,包括DOM
- 任何东西在外的功能
如果一个功能访问其环境,然后该职能是一个封闭。
注意,外层的功能不是必需的,虽然他们提供的好处,我不在这里讨论。通过访问数据在其环境中,一个封闭使这些数据还活着。在subcase的外部/内部的功能、一个外的功能可以创造本地数据,并最终退出,然而,如果任何内部函(s)生存之外的功能退出,那么内部函(s)保持外层功能的当地数据还活着。
例的封闭使用的全球环境:
想象一下这堆溢投票和选票的下按钮事件实现为封锁、voteUp_click和voteDown_click,访问外部变量isVotedUp和isVotedDown,其定义是在全球范围。(为简单起见,我指的是计算器的问题投票按钮,不列的答复表决按钮。)
当用户击VoteUp按钮,voteUp_click功能检查是否isVotedDown==true以确定是否投票,或只是取消一下投票。功能voteUp_click是一个封闭的,因为它是访问其环境。
var isVotedUp = false;
var isVotedDown = false;
function voteUp_click() {
if (isVotedUp)
return;
else if (isVotedDown)
SetDownVote(false);
else
SetUpVote(true);
}
function voteDown_click() {
if (isVotedDown)
return;
else if (isVotedUp)
SetUpVote(false);
else
SetDownVote(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
}
所有的这些功能关闭,因为他们的所有访问他们的环境。
Closures 的作者已经很好地解释了闭包,解释了原因为什么我们需要它们并解释LexicalEnvironment,这是理解闭包所必需的。点击 以下是摘要:
如果访问变量怎么办,但它不是本地的?像这里:
在这种情况下,解释器在中查找变量
外部 LexicalEnvironment
对象。
该过程包括两个步骤:
- 首先,当创建函数f时,它不会创建为空 空间。有一个当前的LexicalEnvironment对象。在这种情况下 上面,它的窗口(a在功能时是未定义的 创建)。 醇>
- 内部函数保留对外部的引用 LexicalEnvironment。
- 内部函数可以从中访问变量 任何时候即使外部功能完成。
- 浏览器将LexicalEnvironment及其所有属性(变量)保留在内存中,直到有一个引用它的内部函数。 醇>
创建函数时,它会获得一个名为[[Scope]]的隐藏属性,该属性引用当前的LexicalEnvironment。
如果读取变量但在任何地方都找不到,则会生成错误。
嵌套功能
函数可以嵌套在另一个函数中,形成一个LexicalEnvironments链,也可以称为范围链。
因此,函数g可以访问g,a和f。
<强>闭包强>
外部函数完成后,嵌套函数可能会继续存在:
标记LexicalEnvironments:
如我们所见, this.say
是用户对象中的属性,因此在用户完成后它仍然存在。
如果你还记得,当 this.say
被创建时,它(作为每个函数)获得一个内部引用 this.say。[[Scope]]
到当前的LexicalEnvironment。因此,当前用户执行的LexicalEnvironment保留在内存中。 User的所有变量也都是它的属性,所以它们也是经常保存的,而不是通常的垃圾。
重点是确保如果内部函数将来想要访问外部变量,它就能够这样做。
总结:
这称为闭包。
作为一个父亲的6岁,目前幼儿教育(以及一个相对新手来编码没有正式的教育,使更正将需要),我认为该课程将坚持最佳的通过实践上播放。如果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最常见的解释是,被称为浏览器。
浏览器:当你连接到互联网的计算机上,片或电话访问的网站,使用浏览器。例子你可以知道是因特网资源管理器、铬、火狐和野生动物园。浏览器就可以理解JavaScript告诉计算机是什么,它需要做。JavaScript说明所谓的功能。
功能:一个功能在JavaScript就像是一家工厂。它可能是一个小工厂只有一个机器里面。或者,它可能包含许多其他小小的工厂,每个都有许多机器做不同的工作。在现实生活中的服装工厂,你可能有大量布架的纹会在和T恤和牛仔裤出来。我们JavaScript工厂仅处理数据,它不能缝、钻孔或熔融金属。在我们的JavaScript的工厂的数据进入和数据。
所有这种数据的东西听起来有点乏味,但这真的是很酷;我们可能有一个功能告诉一个机器人什么做为晚餐。我们说我邀请你和你的朋友到我家。你喜欢鸡腿好的,我喜欢香肠,你的朋友总是想要什么你想和我的朋友不吃肉。
我没时间去购物,所以功能需要知道我们有什么在冰箱上做出决定。每个成份具有不同的烹饪的时间和我们想要的一切服务热机器人在同一时间。我们需要提供的功能,与数据有关我们喜欢的是,该功能可以'谈到冰箱和功能可能控制机器人。
一个功能通常都有一个名称,括号及括号。是这样的:
function cookMeal() { /* STUFF INSIDE THE FUNCTION */ }
注意, /*...*/
和 //
停止码正在读通过浏览器。
名称:你可以叫一个功能只是关于什么的话你想要的。例"cookMeal"是典型的加入两个词放在一起,并给予第二个大写字母开头的-但这不是必要的。它不能有空间,它不可能是一些自己。
括号内:"括号内的"或 ()
都是信箱上JavaScript功能工厂的大门或邮政信箱在街上报文的发送的信息的工厂。有时候在邮箱可能会被标记 例如 cookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime)
, ,在这种情况下你知道是什么数据,你必须给它。
括号:"括号"它看起来像这样 {}
是车窗的我们的工厂。从工厂里的你可以看出来,而是从之外你不能看到。
长码的例子上
我们的代码开始用这个词 功能, 所以我们知道,这是一个!然后名称的功能 唱 -那是我自己的说明的功能。然后括号中 ().括号,总是有一个功能。有时候他们是空的,而有时,他们有的东西。这其中有一个词: (person)
.在此之后有一个括号喜欢这个 {
.这标志着启动的功能 唱().它有一个合作伙伴,这标志着结束 唱() 像这样的 }
function sing(person) { /* STUFF INSIDE THE FUNCTION */ }
所以这个函数可能具有的东西来做唱歌,可能需要一些数据有关的人。它已经指示内要做的事情与这些数据。
现在,在功能 唱(), 近结束的代码线
var person="an old lady";
变量:字母 var 站立的"可变".一个变量是喜欢一个信封。在外面的这个信封的标记为"人"。在它里面含有一个纸条,与我们的信息的功能需要,一些信和空间连接在一起像一块string(它称为一个string),使一个短语阅读"的一个老夫人"。我们的信封中可能包含其他种类的事情相似的数字(称为整数),说明(所谓的功能),列出了(被称为 阵列).因为这个变量写入以外的所有牙套 {}
, 因为你可以看到通过出色的窗口,当您的大括号内,这个变量可以看出,从任何地方的代码。我们呼吁这一全球变量'.
全球变量: 人 是一个全球性的变量,这意味着,如果你改变其价值,从"一个老太太"到"一个年轻的男人", 人 将继续作为一个年轻人,直到你决定要再次进行更改和任何其他功能的代码可以看到,这是一个年轻的男人。按下 F12 按钮或看选项的设置,以开发人员控制台的浏览器和类型的"人"来看看这是什么价值。类型 person="a young man"
要改变它,然后键入"人"再次看到,它已经改变。
在此之后,我们有线
sing(person);
这条线是打电话的功能,因为如果它被叫的狗
"来吧 唱, 过来获得 人!"
当浏览器已经装载JavaScript code一个达到这条线,它将开始的功能。我把线结束时,以确保浏览器具有所需的所有信息。
功能界定的行动-主要功能是关于歌唱。它包含一个称为可变 firstPart 这适用于唱歌的人,适用于每个诗歌的歌曲:"有"+人+"谁吞噬".如果你的类型 firstPart 控制台,你不会得到一个答案,因为可变被锁在一个功能浏览器就可以看不到里面的车窗的括号。
关闭:封锁是较小的功能是内部的很大 唱() 功能。小工厂内大工厂。他们每个人都有自己的括号,这意味着变量内,他们不能从外面看到。这就是为什么名字的变量(生物 和 结果,)可以重复,在封闭但不同的数值。如果这些类型的变量名称在控制台窗口,你不会得到它的价值,因为它是隐藏的通过两个层的车窗。
关闭所有知道什么 唱() 功能的变量 firstPart 是的,因为他们可以看出,从他们的车窗。
之后关闭来的线
fly();
spider();
bird();
cat();
唱()function将呼吁每一个这些职能以便他们给出。然后唱()function的工作将完成。
好的,和一个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 来玩它。
一个六岁孩子的答案(假设他知道一个函数是什么,变量是什么,以及有什么数据):
函数可以返回数据。您可以从函数返回的一种数据是另一种功能。当返回该新函数时,创建它的函数中使用的所有变量和参数都不会消失。相反,该父功能“关闭”。换句话说,没有什么可以看到它内部并看到它使用的变量,除了它返回的函数。这个新函数具有特殊的能力,可以回顾创建它的函数并查看其中的数据。
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
解释它的另一个非常简单的方法是在范围方面:
每当您在较大范围内创建较小的范围时,较小的范围将始终能够看到较大范围内的范围。
一个功能在JavaScript不只是一个参照一套指令(如C语言),但它还包括一个隐藏的数据结构,这是由引用的所有非本地的变量,它使用(捕获的变量)。这种两件功能是所谓的封锁。每个功能在JavaScript可以被认为是一个关闭。
关闭功能的状态。这有点类似"这个"在这个意义上,"这种"也提供了国家一功能,但功能和"这个"单独的对象("这"仅仅是一个奇特的参数,唯一的办法,以结合它永久的功能是创建一个封闭).同时"这种"和功能总是分开住,一个功能不能脱离其封闭和提供的语言没有获得捕获的变量。
因为所有这些外部变量引用的一个词法嵌套功能是当地的实际变量的链中的其词法包围的职能(全球变量可假定为当地变量的一些根本的功能),以及每一个执行功能的创新实例,当地的变量,每一个执行一种功能返回(或其转移出来,例如注册为回) 一套功能创建一种新的封闭(与其自身的潜在的独特的引用非本地的变量表示其执行上下文)。
此外,必须理解,地方变量在JavaScript创建不上堆的框架,但在该堆中和摧毁,只有当没有一个是引用它们。当一个功能返回,对其本地的变量递减,但是它们仍然可以被非null如果在目前的执行他们成为的一部分,一个封闭的和仍然引用的其词法嵌套的职能(其中可能发生的,如果只引用这些嵌套的职能返回或转让给一些外部编码)。
一个例子:
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 variable
var obj = foo(5);
//Extracting functions just to show that no 'this' is involved here
var getValue = obj.getValue;
var setValue = obj.setValue;
alert(getValue()); //Displays 5
setValue(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();
也许除了最早熟的六岁孩子之外,还有一些例子,但是有一些例子帮助我在JavaScript中实现了关闭概念。
闭包是一个可以访问另一个函数范围(它的变量和函数)的函数。创建闭包的最简单方法是使用函数内的函数;原因是在JavaScript中,函数始终可以访问其包含的函数&#8217;范围。
function outerFunction() {
var outerVar = "monkey";
function innerFunction() {
alert(outerVar);
}
innerFunction();
}
outerFunction();
警告:猴子
在上面的例子中,调用了outerFunction,后者又调用了innerFunction。注意innerVar如何可用于innerFunction,通过正确警告outerVar的值来证明。
现在考虑以下事项:
function outerFunction() {
var outerVar = "monkey";
function innerFunction() {
return outerVar;
}
return innerFunction;
}
var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());
警告:猴子
referenceToInnerFunction设置为outerFunction(),它只返回对innerFunction的引用。当调用referenceToInnerFunction时,它返回outerVar。同样,如上所述,这表明innerFunction可以访问outerVar的变量outerVunction。此外,值得注意的是,即使在outerFunction完成执行后,它仍保留此访问权限。
这里的事情变得非常有趣。如果我们要删除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将其范围(其变量和函数)的引用添加到其作用域链中。这意味着innerFunction具有指向所有outerFunction变量的指针或引用,包括outerVar。因此,即使在outerFunction完成执行时,或者即使它被删除或设置为null,其范围中的变量(如outerVar)也会留在内存中,因为在返回到的内部函数部分上对它们的未完成引用referenceToInnerFunction。要从内存中真正释放outerVar和其余的外部函数变量,你必须摆脱对它们的这种杰出引用,比如将referenceToInnerFunction设置为null。
//////////
关于闭包的另外两件事要注意。首先,闭包将始终可以访问其包含函数的最后一个值。
function outerFunction() {
var outerVar = "monkey";
function innerFunction() {
alert(outerVar);
}
outerVar = "gorilla";
innerFunction();
}
outerFunction();
警告:大猩猩
其次,当创建一个闭包时,它会保留对其所有封闭函数的变量和函数的引用;它不会挑选。但是,应该谨慎使用封闭装置,或者至少要小心使用,因为它们可能会占用大量内存;在包含函数执行完毕后,很多变量可以保存在内存中。
我只是将它们指向 Mozilla Closures页面。这是我发现的关闭基础知识和实际用法的最佳,最简单,最简单的解释。强烈建议任何学习JavaScript的人。
是的,我甚至推荐给一个6岁的孩子 - 如果这个6岁的孩子正在学习闭包,那么他们已经准备好理解简洁明了的解释了在文章中提供。