我问了一个关于curry和闭包的问题。 什么是闭包?它和咖喱有什么关系?
当前回答
从Lua.org:
当一个函数被封装在另一个函数中编写时,它可以完全访问封装函数中的局部变量;这个特性称为词法作用域。尽管这听起来很明显,但事实并非如此。词法作用域加上第一类函数是编程语言中一个强大的概念,但很少有语言支持这个概念。
其他回答
闭包为JavaScript提供状态。
状态在编程中仅仅意味着记忆。
例子
var a = 0;
a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3
在上面的例子中,state存储在变量“a”中。我们在a后面加几次1。我们之所以能够这样做,是因为我们能够“记住”这个值。状态符“a”将该值保存在内存中。
通常,在编程语言中,您希望跟踪事物、记住信息并在以后访问它。
在其他语言中,这通常是通过使用类来实现的。类,就像变量一样,跟踪它的状态。而该类的实例,依次在它们内部也有状态。状态仅仅意味着您以后可以存储和检索的信息。
例子
class Bread {
constructor (weight) {
this.weight = weight;
}
render () {
return `My weight is ${this.weight}!`;
}
}
我们如何从“渲染”方法中访问“权重”?好吧,感谢国家。Bread类的每个实例都可以通过从“状态”(内存中存储信息的地方)读取它来呈现自己的权重。
JavaScript是一种非常独特的语言,它在历史上没有类(现在有了,但在底层只有函数和变量),所以闭包为JavaScript提供了一种记忆事物并在以后访问它们的方法。
例子
var n = 0;
var count = function () {
n = n + 1;
return n;
};
count(); // # 1
count(); // # 2
count(); // # 3
上面的例子实现了用变量“保持状态”的目标。这太棒了!然而,这有一个缺点,即变量(“状态”holder)现在是公开的。我们可以做得更好。我们可以使用闭包。
例子
var countGenerator = function () {
var n = 0;
var count = function () {
n = n + 1;
return n;
};
return count;
};
var count = countGenerator();
count(); // # 1
count(); // # 2
count(); // # 3
这太棒了。
现在我们的count函数可以计数了。它之所以能够这样做,是因为它可以“保持”状态。这种情况下的状态是变量“n”。这个变量现在关闭了。在时间和空间上封闭。因为你永远无法恢复它,改变它,给它赋值或直接与它交互。在空间中,因为它在地理上嵌套在“countGenerator”函数中。
为什么这很神奇?因为不需要涉及任何其他复杂的工具(例如类、方法、实例等),我们就能够 1. 隐藏 2. 从远处控制
我们隐藏了状态,变量“n”,这使它成为一个私有变量! 我们还创建了一个API,可以以预定义的方式控制这个变量。特别地,我们可以像这样调用API“count()”,它从“距离”将1加到“n”。除非通过API,否则任何人都无法访问“n”。
JavaScript的简单性确实令人惊叹。
闭包是其中一个重要原因。
下面是另一个现实生活中的例子,使用了游戏中流行的脚本语言——Lua。我需要稍微改变一个库函数的工作方式,以避免stdin不可用的问题。
local old_dofile = dofile
function dofile( filename )
if filename == nil then
error( 'Can not use default of stdin.' )
end
old_dofile( filename )
end
当这段代码完成它的作用域时,old_dofile的值就消失了(因为它是本地的),但是该值已经被封装在一个闭包中,所以新的重新定义的dofile函数可以访问它,或者更确切地说,作为“upvalue”与函数一起存储的副本。
闭包是JavaScript中的一个特性,函数可以访问自己的作用域变量、外部函数变量和全局变量。
闭包即使在外部函数返回之后也可以访问它的外部函数作用域。这意味着闭包可以记住并访问它的外部函数的变量和参数,即使函数已经完成。
内部函数可以访问在自己的作用域、外部函数的作用域和全局作用域中定义的变量。外部函数可以访问在自己的作用域和全局作用域中定义的变量。
闭包的例子:
var globalValue = 5;
function functOuter() {
var outerFunctionValue = 10;
//Inner function has access to the outer function value
//and the global variables
function functInner() {
var innerFunctionValue = 5;
alert(globalValue + outerFunctionValue + innerFunctionValue);
}
functInner();
}
functOuter();
输出将为20,即其内部函数自身变量、外部函数变量和全局变量值的和。
封闭非常简单。我们可以这样考虑: 闭包=函数+它的词汇环境
考虑以下函数:
function init() {
var name = “Mozilla”;
}
在上述情况下,闭包是什么? 函数init()及其词法环境中的变量,即name。 闭包= init() + name
考虑另一个函数:
function init() {
var name = “Mozilla”;
function displayName(){
alert(name);
}
displayName();
}
这里的闭包是什么? 内部函数可以访问外部函数的变量。displayName()可以访问父函数init()中声明的变量名。但是,如果displayName()中存在相同的局部变量,则将使用它们。
闭包1:init函数+ (name变量+ displayName()函数)——>词法作用域
闭包2:displayName函数+(名称变量)——>词法范围
下面的示例演示了Scheme编程语言中的闭包。
首先定义一个函数,该函数定义了一个局部变量,在函数外部不可见。
; Function using a local variable
(define (function)
(define a 1)
(display a) ; prints 1, when calling (function)
)
(function) ; prints 1
(display a) ; fails: a undefined
下面是同样的例子,但是现在函数使用了一个全局变量,定义在函数外部。
; Function using a global variable
(define b 2)
(define (function)
(display b) ; prints 2, when calling (function)
)
(function) ; prints 2
(display 2) ; prints 2
最后,这里有一个函数携带自己闭包的例子:
; Function with closure
(define (outer)
(define c 3)
(define (inner)
(display c))
inner ; outer function returns the inner function as result
)
(define function (outer))
(function) ; prints 3