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


当前回答

在Groovy中有一个简单的例子供您参考:

def outer() {
    def x = 1
    return { -> println(x)} // inner
}
def innerObj = outer()
innerObj() // prints 1

其他回答

请看看下面的代码,以更深入地理解闭包:

        for(var i=0; i< 5; i++){            
            setTimeout(function(){
                console.log(i);
            }, 1000);                        
        }

这里会输出什么?0 1 2 3 4不是5 5 5 5 5 5 5 5 5因为是闭包的

那么它将如何解决呢?答案如下:

       for(var i=0; i< 5; i++){
           (function(j){     //using IIFE           
                setTimeout(function(){
                               console.log(j);
                           },1000);
            })(i);          
        }

让我简单解释一下,当一个函数创建时,直到它在第一个代码中调用5次for循环才会发生,但不会立即调用所以当它在1秒后调用i.e时,这也是异步的,所以在这个for循环结束之前,将值5存储在var i中最后执行5次setTimeout函数并打印5,5,5,5,5

这里如何解决使用IIFE即立即调用函数表达式

       (function(j){  //i is passed here           
            setTimeout(function(){
                           console.log(j);
                       },1000);
        })(i);  //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4

要了解更多,请了解执行上下文以理解闭包。

还有一个解决方案来解决这个问题,使用let (ES6特性),但在引子下面,上面的函数是工作的 (让我= 0;我< 5;我+ +){ setTimeout(函数(){ console.log(我); }, 1000); } 输出:0,1,2,3,4

更多解释:

在内存中,当for循环执行时,图片如下所示:

1)循环

     setTimeout(function(){
                    console.log(i);
                },1000);  

2)循环

     setTimeout(function(){
                    console.log(i);
                },1000); 

3)循环

     setTimeout(function(){
                    console.log(i);
                },1000); 

4)循环

     setTimeout(function(){
                    console.log(i);
                },1000); 

循环5)

     setTimeout(function(){
                    console.log(i);
                },1000);  

这里没有执行i,然后在完成循环后,var i在内存中存储值5,但它的作用域在它的子函数中始终可见,因此当函数在setTimeout内执行五次时,它打印5,5,5,5,5

所以解决这个问题使用IIFE如上所述。

在正常情况下,变量受作用域规则约束:局部变量仅在定义的函数内工作。闭包是为了方便而暂时打破这一规则的一种方式。

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。

变量作用域

声明局部变量时,该变量具有作用域。一般来说,局部变量只存在于声明它们的块或函数中。

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在内部函数的作用域内。

正常情况下,当一个函数退出时,它的所有局部变量都会消失。但是,如果我们返回inner函数并将其赋值给变量fnc,以便在outer退出后它仍然存在,那么定义inner时在作用域内的所有变量也将仍然存在。变量a已经被关闭了——它在一个闭包中。

注意变量a对于fnc是完全私有的。这是在函数式编程语言(如JavaScript)中创建私有变量的一种方式。

正如你可能猜到的那样,当我调用fnc()时,它会打印a的值,即“1”。

在没有闭包的语言中,当函数outer退出时,变量a将被垃圾收集并丢弃。调用fnc将抛出一个错误,因为a不再存在。

在JavaScript中,变量a持续存在,因为变量作用域是在函数第一次声明时创建的,并且只要函数继续存在,变量a就会持续存在。

A属于outer的范围。inner的作用域有一个父指针指向outer的作用域。FNC是一个指向内的变量。只要FNC存在,A就存在。A在闭包内。

进一步阅读(观看)

我制作了一个YouTube视频,介绍了这段代码的一些实际使用示例。

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

闭包为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的简单性确实令人惊叹。

闭包是其中一个重要原因。