我问了一个关于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如上所述。

这里有一个现实世界的例子,为什么闭包这么厉害…这是我的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回调发生时,闭包仍然维护这些变量,因此它可以使用原始参数调用原始函数。 这是一种咖喱,或功能装饰。

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

闭包是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,即其内部函数自身变量、外部函数变量和全局变量值的和。

如果您来自Java世界,您可以将闭包与类的成员函数进行比较。看看这个例子

var f=function(){
  var a=7;
  var g=function(){
    return a;
  }
  return g;
}

函数g是一个闭包:g封闭a。g可以和成员函数比较,a可以和类域比较,函数f可以和类比较。

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

考虑以下函数:

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