我问了一个关于Currying和闭包的问题。什么是闭包?它与currying有什么关系?
发布于 2011-09-19 05:04:53
变量作用域
当您声明一个局部变量时,该变量有一个作用域。通常,局部变量只存在于声明它们的块或函数中。
function() {
var a = 1;
console.log(a); // works
}
console.log(a); // fails如果我试图访问一个局部变量,大多数语言都会在当前作用域中查找它,然后向上遍历父作用域,直到它们到达根作用域。
var a = 1;
function() {
console.log(a); // works
}
console.log(a); // works当一个块或函数结束时,它的局部变量就不再需要了,并且通常会耗尽内存。
这就是我们通常期望的工作方式。
闭包是一个持久的局部变量作用域
闭包是一个持久化的作用域,即使在代码执行移出该块之后,它也会保留局部变量。支持闭包的语言(如JavaScript、Swift和Ruby)将允许您保留对某个作用域(包括其父作用域)的引用,即使在声明这些变量的块完成执行之后也是如此,前提是您在某个地方保留了对该块或函数的引用。
作用域对象及其所有局部变量都绑定到该函数,只要该函数继续存在,它们就会一直存在。
这为我们提供了函数的可移植性。我们可以预期,当第一次定义函数时,任何在作用域中的变量在以后调用该函数时仍然在作用域中,即使我们在完全不同的上下文中调用该函数。
例如
下面是一个用JavaScript编写的非常简单的示例,它说明了这一点:
outer = function() {
var a = 1;
var inner = function() {
console.log(a);
}
return inner; // this returns a function
}
var fnc = outer(); // execute outer to get inner
fnc();在这里,我在函数中定义了一个函数。内部函数可以访问外部函数的所有局部变量,包括a。变量a在内部函数的作用域内。
通常,当一个函数退出时,它的所有局部变量都会被清除。但是,如果我们返回内部函数并将其赋给一个变量fnc,以便它在outer退出后仍然存在,那么在定义时,所有在作用域中的变量也将持久化。变量a已被关闭--它在一个闭包内。
注意,对于fnc来说,变量a是完全私有的。这是在函数式编程语言(如JavaScript )中创建私有变量的一种方法。
您可能会猜到,当我调用fnc()时,它会输出a的值,即"1“。
在没有闭包的语言中,当函数outer退出时,变量a将被垃圾回收并丢弃。调用fnc会抛出一个错误,因为a不再存在。
在JavaScript中,变量a会一直存在,因为变量scope是在第一次声明函数时创建的,并且只要函数继续存在,变量scope就会一直存在。
a属于outer的范围。inner的作用域有一个指向outer作用域的父指针。fnc是一个指向inner的变量。只要fnc存在,a就会一直存在。a在闭包中。
进一步阅读(观看)
我用一些实际的用法示例查看了这段代码,并制作了一个YouTube video。
发布于 2008-08-31 04:49:37
我将给出一个示例(在JavaScript中):
function makeCounter () {
var count = 0;
return function () {
count += 1;
return count;
}
}
var x = makeCounter();
x(); returns 1
x(); returns 2
...etc...这个函数,makeCounter,会返回一个函数,我们称之为x,它每次被调用都会加1。因为我们没有为x提供任何参数,所以它必须以某种方式记住计数。它知道在哪里根据所谓的词法作用域来查找它-它必须查找它定义的位置来查找值。这个“隐藏”值就是所谓的闭包。
下面是我再次使用的例子:
function add (a) {
return function (b) {
return a + b;
}
}
var add3 = add(3);
add3(4); returns 7您可以看到,当您使用参数a(即3)调用add时,该值包含在我们定义为add3的返回函数的闭包中。这样,当我们调用add3时,它知道在哪里找到要执行加法的a值。
发布于 2016-04-27 10:33:56
首先,与这里大多数人告诉你的相反,闭包是函数闭包而不是函数闭包( a !)那么这是什么呢?
它是在函数的“周围上下文”(称为其环境)中定义的一组符号,这些符号使其成为封闭表达式(即,在该表达式中,每个符号都定义了一个值,因此可以对其求值)。
例如,当您有一个JavaScript函数时:
function closed(x) {
return x + 3;
}它是一个封闭表达式,因为出现在其中的所有符号都在其中定义(它们的含义很清楚),因此您可以对其求值。换句话说,它是自包含的。
但是如果你有一个像这样的函数:
function open(x) {
return x*y + 3;
}它是一个开放表达式,因为其中有尚未定义的符号。也就是说,y。当我们看这个函数时,我们不知道y是什么,它的意思是什么,我们不知道它的值,所以我们不能计算这个表达式。也就是说,我们不能调用这个函数,直到我们告诉它y应该是什么意思。这个y被称为自由变量。
这个y需要一个定义,但这个定义不是函数的一部分-它是在其他地方定义的,在它的“周围环境”(也称为环境)中。至少这是我们所希望的:
例如,可以全局定义它:
var y = 7;
function open(x) {
return x*y + 3;
}或者可以在包装它的函数中定义它:
var global = 2;
function wrapper(y) {
var w = "unused";
return function(x) {
return x*y + 3;
}
}环境中赋予表达式中自由变量含义的部分是闭包。它被称为这种方式,因为它通过为所有自由变量提供这些缺失的定义,将开放表达式转换为封闭表达式,以便我们可以对其进行计算。
在上面的例子中,内部函数(我们没有给它命名,因为我们不需要它)是一个开放表达式,因为其中的变量y是自由的-它的定义在函数外部,在包装它的函数中。该匿名函数的环境是变量集:
{
global: 2,
w: "unused",
y: [whatever has been passed to that wrapper function as its parameter `y`]
}现在,闭包是这个环境的一部分,它通过提供所有自由变量的定义来关闭内部函数。在我们的例子中,内部函数中唯一的自由变量是y,所以该函数的闭包是它的环境的这个子集:
{
y: [whatever has been passed to that wrapper function as its parameter `y`]
}环境中定义的其他两个符号不是该函数闭包的一部分,因为它不需要它们运行。不需要它们来关闭它。
更多关于这背后的理论:https://stackoverflow.com/a/36878651/434562
值得注意的是,在上面的示例中,包装器函数将其内部函数作为值返回。从定义(或创建)函数的那一刻起,我们调用这个函数的时刻就可能是遥远的。特别是,它的包装函数不再运行,它在调用堆栈上的参数也不再存在:P这就产生了问题,因为内部函数在被调用时需要y!换句话说,它需要闭包中的变量以某种方式超过包装器函数,并在需要时出现在那里。因此,内部函数必须创建这些变量的快照,这些变量使其闭包,并将它们存储在安全的地方以供以后使用。(调用堆栈之外的某个位置。)
这就是为什么人们经常混淆术语闭包是一种特殊类型的函数,它可以对他们使用的外部变量进行快照,或者是用于存储这些变量的数据结构。但我希望你现在明白,它们不是闭包本身-它们只是在编程语言中实现闭包的方法,或者是允许来自函数闭包的变量在需要时存在的语言机制。关于闭包有很多误解,这(不必要地)使这个主题比实际情况更令人困惑和复杂。
https://stackoverflow.com/questions/36636
复制相似问题