如何向某人解释JavaScript关闭,知道它们所构成的概念(例如功能、变量等),但不了解关闭本身?
我已经在维基百科上看到了图表的例子,但不幸的是,它没有帮助。
如何向某人解释JavaScript关闭,知道它们所构成的概念(例如功能、变量等),但不了解关闭本身?
我已经在维基百科上看到了图表的例子,但不幸的是,它没有帮助。
当前回答
MDN 解释我认为最好的:
关闭是指独立(自由)变量的函数,换句话说,关闭中定义的函数“记得”它创造的环境。
关闭总是有一个外部功能和一个内部功能. 内部功能是所有工作发生的地方,而外部功能只是保留内部功能创建的范围的环境。 这样,关闭的内部功能“记得”它创建的环境/范围。
var closure = function() {
var count = 0;
return function() {
count++;
console.log(count);
};
};
var counter = closure();
counter() // returns 1
counter() // returns 2
counter() // returns 3
在上面的代码中,计算是由外部函数(环境函数)保存的,所以每次你呼叫计算(),内部函数(工作函数)可以增加它。
其他回答
好吧,和一个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
正如你可以看到的那样,房间里留下的玩具仍然可以通过兄弟访问,不管房间是否被锁定。
我思考关闭的越多,我会看到它作为一个2步过程: init - 行动
init: pass first what's needed...
action: in order to achieve something for later execution.
到6岁时,我会强调关闭的实际方面:
Daddy: Listen. Could you bring mum some milk (2).
Tom: No problem.
Daddy: Take a look at the map that Daddy has just made: mum is there and daddy is here.
Daddy: But get ready first. And bring the map with you (1), it may come in handy
Daddy: Then off you go (3). Ok?
Tom: A piece of cake!
例如:把一些牛奶带到妈妈(=行动)。首先做好准备,然后把地图(=init)带到这里。
function getReady(map) {
var cleverBoy = 'I examine the ' + map;
return function(what, who) {
return 'I bring ' + what + ' to ' + who + 'because + ' cleverBoy; //I can access the map
}
}
var offYouGo = getReady('daddy-map');
offYouGo('milk', 'mum');
因为如果你带着一个非常重要的信息(地图),你有足够的知识来执行其他类似的操作:
offYouGo('potatoes', 'great mum');
对于一个开发人员来说,我会在关闭和OOP之间进行平行。 init 阶段类似于在传统 OO 语言中向构建者传达论点; 行动阶段最终是你呼吁实现你想要的方法。
请参见我的另一个答案,描述OO和关闭之间的平行性:
如何在JavaScript中“正确”创建自定义对象?
维基百科 关闭:
在计算机科学中,关闭是一种函数,以及该函数的非本地名称(自由变量)的参考环境。
技术上,在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] } // 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)并后续执行,每次被召唤时产生一个数据库函数. 每一个所产生的函数都会有自己的隐藏的数据库对象. 另一个使用例子关闭是当我们不返回一个函数时,但包含多个函数的对象
函数和函数的外部范围(语法环境)的参考
函数 汽车(制造商,模型,年,颜色) { 返回 { 到String() { 返回 `${ 制造商} ${ 模型} (${ 年}, ${ 颜色})` } } 组车 = 新车(‘Aston Martin’,‘V8 Vantage’,‘2012’,‘Quantum Silver’) console.log(car.toString())
事件导向的编程
在下面的例子中,所有实施细节都隐藏在即时执行的函数表达式内。 函数标记和 toString 接近私人状态和函数他们需要完成工作。 关闭已使我们能够模块化和包容我们的代码。
例子1
这个例子表明,本地变量在关闭中没有复制:关闭保持了对原始变量的参考。
左边
TLDR
细节
当一个函数通过内部(呼叫)方法引用时,函数对象的环境参考将复制到新创建的执行框架(stack frame)环境记录的外部环境参考。
在下面的例子中,函数f 关闭了全球执行背景的语法环境:
function f() {}
在下面的例子中,函数h在函数g的语法环境中关闭,而函数h则在全球执行背景的语法环境中关闭。
function g() {
function h() {}
}
如果内部函数由外部函数返回,则外部语法环境在外部函数返回后将继续存在,因为如果内部函数最终被召回,则外部语法环境必须可用。