我问了一个关于curry和闭包的问题。 什么是闭包?它和咖喱有什么关系?


当前回答

封闭非常简单。我们可以这样考虑: 闭包=函数+它的词汇环境

考虑以下函数:

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函数+(名称变量)——>词法范围

其他回答

闭包是一个可以引用另一个函数中的状态的函数。例如,在Python中,它使用闭包"inner":

def outer (a):
    b = "variable in outer()"
    def inner (c):
        print a, b, c
    return inner

# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1

这里有一个现实世界的例子,为什么闭包这么厉害…这是我的Javascript代码。让我举例说明。

Function.prototype.delay = function(ms /*[, arg...]*/) {
  var fn = this,
      args = Array.prototype.slice.call(arguments, 1);

  return window.setTimeout(function() {
      return fn.apply(fn, args);
  }, ms);
};

下面是你如何使用它:

var startPlayback = function(track) {
  Player.play(track);  
};
startPlayback(someTrack);

现在,假设您希望回放延迟开始,例如在此代码段运行后5秒。这对于延迟来说是很容易的,它是封闭的:

startPlayback.delay(5000, someTrack);
// Keep going, do other things

当你调用delay为5000ms时,第一个代码段运行,并将传入的参数存储在它的闭包中。然后5秒后,当setTimeout回调发生时,闭包仍然维护这些变量,因此它可以使用原始参数调用原始函数。 这是一种咖喱,或功能装饰。

如果没有闭包,您将不得不以某种方式在函数外部维护这些变量的状态,从而将函数外部的代码丢弃在逻辑上属于函数内部的代码中。使用闭包可以极大地提高代码的质量和可读性。

不包含自由变量的函数称为纯函数。

包含一个或多个自由变量的函数称为闭包。

var pure = function pure(x){
  return x 
  // only own environment is used
}

var foo = "bar"

var closure = function closure(){
  return foo 
  // foo is a free variable from the outer environment
}

src: https://leanpub.com/javascriptallongesix/read # leanpub-auto-if-functions-without-free-variables-are-pure-are-closures-impure

我将给出一个例子(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值来执行加法。

凯尔的回答很好。我认为唯一需要澄清的是,闭包基本上是lambda函数创建时堆栈的快照。然后,当函数重新执行时,堆栈将恢复到执行函数之前的状态。因此,正如Kyle提到的,当lambda函数执行时,隐藏值(count)是可用的。