我问了一个关于curry和闭包的问题。 什么是闭包?它和咖喱有什么关系?
当前回答
闭包 只要在另一个函数内部定义了一个函数,内部函数就可以访问声明的变量 在外层函数中。闭包最好用例子来解释。 在清单2-18中,可以看到内部函数可以访问变量variableInOuterFunction 外的范围。外部函数中的变量已被内部函数封闭(或绑定在)。因此才有了这个术语 关闭。这个概念本身很简单,也很直观。
Listing 2-18:
function outerFunction(arg) {
var variableInOuterFunction = arg;
function bar() {
console.log(variableInOuterFunction); // Access a variable from the outer scope
}
// Call the local function to demonstrate that it has access to arg
bar();
}
outerFunction('hello closure!'); // logs hello closure!
来源:http://index-of.es/Varios/Basarat%20Ali%20Syed%20 (auth)。他们% 20 node.js-apress % 20 (2014) . pdf
其他回答
在正常情况下,变量受作用域规则约束:局部变量仅在定义的函数内工作。闭包是为了方便而暂时打破这一规则的一种方式。
def n_times(a_thing)
return lambda{|n| a_thing * n}
end
在上面的代码中,lambda(|n| a_thing * n}是闭包,因为a_thing是由lambda(匿名函数创建者)引用的。
现在,如果你把得到的匿名函数放到一个函数变量中。
foo = n_times(4)
Foo将打破正常的作用域规则,开始在内部使用4。
foo.call(3)
返回12。
首先,与这里大多数人告诉你的相反,闭包不是一个函数!那么它是什么呢? 它是在函数的“周围上下文”(称为其环境)中定义的一组符号,使其成为CLOSED表达式(即,每个符号都被定义并具有值的表达式,因此可以对其求值)。
例如,当你有一个JavaScript函数:
function closed(x) {
return x + 3;
}
它是一个封闭表达式,因为其中出现的所有符号都在其中定义(它们的含义是明确的),所以您可以对其求值。换句话说,它是独立的。
但如果你有一个这样的函数
function open(x) {
return x*y + 3;
}
它是一个开放的表达式,因为其中有尚未定义的符号。也就是y,当我们看这个函数的时候,我们不知道y是什么,它意味着什么,我们不知道它的值,所以我们不能计算这个表达式。也就是说,我们不能调用这个函数,直到我们知道y在其中的含义。这个y叫做自由变量。
这需要一个定义,但这个定义不是函数的一部分——它是在其他地方定义的,在它的“周围上下文”(也称为环境)中。至少这是我们所希望的:P
例如,它可以被全局定义:
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
It's worth to note that in the example above, the wrapper function returns its inner function as a value. The moment we call this function can be remote in time from the moment the function has been defined (or created). In particular, its wrapping function is no longer running, and its parameters which has been on the call stack are no longer there :P This makes a problem, because the inner function needs y to be there when it is called! In other words, it requires the variables from its closure to somehow outlive the wrapper function and be there when needed. Therefore, the inner function has to make a snapshot of these variables which make its closure and store them somewhere safe for later use. (Somewhere outside the call stack.)
And this is why people often confuse the term closure to be that special type of function which can do such snapshots of the external variables they use, or the data structure used to store these variables for later. But I hope you understand now that they are not the closure itself – they're just ways to implement closures in a programming language, or language mechanisms which allows the variables from the function's closure to be there when needed. There's a lot of misconceptions around closures which (unnecessarily) make this subject much more confusing and complicated than it actually is.
如果您来自Java世界,您可以将闭包与类的成员函数进行比较。看看这个例子
var f=function(){
var a=7;
var g=function(){
return a;
}
return g;
}
函数g是一个闭包:g封闭a。g可以和成员函数比较,a可以和类域比较,函数f可以和类比较。
闭包为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的简单性确实令人惊叹。
闭包是其中一个重要原因。
凯尔的回答很好。我认为唯一需要澄清的是,闭包基本上是lambda函数创建时堆栈的快照。然后,当函数重新执行时,堆栈将恢复到执行函数之前的状态。因此,正如Kyle提到的,当lambda函数执行时,隐藏值(count)是可用的。