闭包基本上是嵌套在函数A中的函数B,它可以访问A的局部变量:
function A() {
var x = 5;
function B() {
print(x);
}
return B;
}
Run Code Online (Sandbox Code Playgroud)
如果你来自C++背景,这很难消化.当我们尝试调用A返回的函数时,x它会引用什么?x自A()终止后不会无效吗?
答案是x实际存在.那B我们实际上返回携带x含蓄与它周围.很酷,嗯?
在更一般的意义上,闭包是绑定到某些数据的函数.在没有像C这样的闭包的语言中,每个程序都有固定数量的函数.对于闭包,在某种意义上,您可以通过将它们绑定到动态数据来"创建函数".当然,没有人会阻止你在C中模仿闭包,但有时可能会很痛苦.
闭包非常有用.例如,假设我们想要实现我们自己的"for循环":
function loop(count, f) {
for (var i = 0; i < count; i++)
f(i);
}
var array = [0,1,2,3,4];
loop(5, function(i) {
print(array[i]);
});
Run Code Online (Sandbox Code Playgroud)
允许访问外部变量而不做一堆额外的废话让代码变得简单.如果没有闭包,您可能必须将上下文变量传递给loop函数:
function loop(count, f, ctx) {
for (var i = 0; i < count; i++)
f(i, ctx);
}
var array = [0,1,2,3,4];
loop(5, function(i, ctx) {
print(ctx[i]);
}, array);
Run Code Online (Sandbox Code Playgroud)
另一个例子:假设我们想在jQuery中注册一个回调,但它可能会在函数及其调用者及其调用者的调用者运行完毕后很久执行:
$(document).ready(function(){
var clicked = 0;
$('#button').click(function(){
clicked++;
alert("You've pressed the button " + clicked + " times.");
});
});
Run Code Online (Sandbox Code Playgroud)
如果JavaScript更像是C++(在C++ 0x之前),那么在调用clicked赋值函数时,该变量就会消失$(document).ready(),而单击回调的按钮会有未定义的行为.
这是我对闭包的看法......
函数对象由两部分组成。第一件事是函数的代码,第二件事是它执行的范围。在闭包中,函数执行的范围和代码是相互分离的。相同的代码可以在不同的范围内执行。
如果以完全不受限制的方式允许这样做,将会导致极大的混乱。即使它像动态作用域一样松散(函数继承了调用它的地方的作用域),它也会变得非常混乱。
闭包是指定范围规则的一种便捷方式,这种方式很有意义,因为它们只需要读取代码而不需要跟踪它。在闭包中,函数获得声明它的范围。如果在执行另一个函数时声明它,它将获取该函数堆栈的特定实例的范围。这比能够给出闭包和任意作用域或动态作用域要简单得多,更容易理解。
下面是 Python 中一个简单闭包的示例:
def outer():
def closure():
pass
return closure
Run Code Online (Sandbox Code Playgroud)
这里的函数closure是一个闭包。它实际上并没有使用定义它的范围中的任何变量,因此它非常简单,但它仍然是一个。
这是 Python 中的一个不那么简单但仍然简单的闭包:
def outer(x):
def closure():
return x
return closure
f1 = outer(5)
f2 = outer(6)
Run Code Online (Sandbox Code Playgroud)
调用f1()将返回 5,调用f2()将返回 6。如您所见,该函数closure部分是代码,部分是作用域。
调用时,它会为调用创建一个堆栈条目,其中包含保存值 5 的outer(5)变量的版本。然后它声明获取该范围的函数。xclosure
当被调用时,它会为调用创建一个堆栈条目,其中包含保存值 6outer(6)的变量的版本。然后它声明获取该范围的函数。xclosure