什么是'关闭'?

时间:2008-08-31 04:38:21

标签: functional-programming computer-science terminology glossary

我问了一个关于Currying和关闭的问题。 什么是关闭?它与currying有什么关系?

24 个答案:

答案 0 :(得分:608)

可变范围

声明局部变量时,该变量具有范围。通常,局部变量仅存在于声明它们的块或函数中。

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在内部函数的范围内。

通常,当一个函数退出时,它的所有局部变量都会被吹掉。但是,如果我们返回内部函数并将其分配给变量fnc,以便在outer退出后它仍然存在, inner时范围内的所有变量定义也坚持。变量a已被关闭 - 它在一个闭包内。

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

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

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

在JavaScript中,变量a仍然存在,因为变量范围是在首次声明函数时创建的,并且只要函数继续存在就会持续存在。

a属于outer的范围。 inner的范围具有指向outer范围的父指针。 fnc是一个指向inner的变量。只要a持续存在,fnc就会一直存在。 a在关闭范围内。

答案 1 :(得分:85)

我将给出一个示例(在JavaScript中):

function makeCounter () {
  var count = 0;
  return function () {
    count += 1;
    return count;
  }
}

var x = makeCounter();

x(); returns 1

x(); returns 2

...etc...

这个函数makeCounter做的是它返回一个函数,我们称之为x,每次调用它时都会计数一次。由于我们没有向x提供任何参数,因此它必须以某种方式记住计数。它知道在何处根据所谓的词法范围找到它 - 它必须查找定义为找到值的位置。这个“隐藏”值就是所谓的闭包。

这是我再次讨论的例子:

function add (a) {
  return function (b) {
    return a + b;
  }
}

var add3 = add(3);

add3(4); returns 7

您可以看到,当您使用参数a(为3)调用add时,该值包含在我们定义为add3的返回函数的闭包中。这样,当我们调用add3时,它知道在哪里找到一个值来执行添加。

答案 2 :(得分:54)

Kyle's answer非常好。我认为唯一的另一个澄清是闭包基本上是创建lambda函数时堆栈的快照。然后,当重新执行该函数时,堆栈将在执行该函数之前恢复到该状态。因此,正如凯尔所提到的,当lambda函数执行时,隐藏值(count)可用。

答案 3 :(得分:28)

首先,与大多数人告诉你的相反,关闭不是一个功能!那么呢? 它是在函数的“周围上下文”(称为 environment )中定义的符号的,它使其成为CLOSED表达式(即,每个符号的表达式)已定义并具有值,因此可以对其进行评估。

例如,当你有一个JavaScript函数时:

function closed(x) {
  return x + 3;
}

它是闭合表达式,因为它中出现的所有符号都在其中定义(它们的含义很清楚),因此您可以对其进行评估。换句话说,它是自包含的

但是如果你有这样的功能:

function open(x) {
  return x*y + 3;
}

它是一个打开表达式,因为其中有符号尚未定义。即,y。在查看此函数时,我们无法分辨y是什么,它是什么意思,我们不知道它的值,所以我们无法评估这个表达式。即在我们告诉y在其中意味着什么之前,我们不能调用此函数。此y称为自由变量

y需要定义,但此定义不是函数的一部分 - 它在其他地方定义,在其“周围环境”(也称为 environment )中。至少这是我们所希望的: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;
   }

}

在表达式中赋予自由变量意义的环境部分是闭包。它以这种方式调用,因为它通过为所有自由变量提供这些缺少的定义,将 open 表达式转换为已关闭表达式,这样我们就可以对它进行评估。

在上面的示例中,内部函数(我们没有给出名称,因为我们不需要它)是 open表达式,因为其中的变量y free - 它的定义在函数之外,在包装它的函数中。该匿名函数的环境是一组变量:

{
  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

值得注意的是,在上面的示例中,包装函数将其内部函数作为值返回。从定义(或创建)函数的那一刻起,我们调用此函数的时刻可以是远程的。特别是,它的包装函数不再运行,并且它在调用堆栈上的参数不再存在:P这会产生问题,因为内部函数在调用时需要y!换句话说,它需要从其闭包的变量以某种方式比包装函数更长并在需要时存在。因此,内部函数必须创建这些变量的快照,这些变量使其闭合并将它们存储在安全的地方供以后使用。 (在调用堆栈之外的某处。)

这就是为什么人们常常将术语 closure 混淆为特殊类型的函数,它可以对它们使用的外部变量进行快照,或者用于存储这些变量的数据结构以供日后使用。但我希望你现在理解他们闭包本身 - 他们只是在编程语言中实现闭包的方法,或者是允许来自函数的闭包在需要时就在那里。关于闭包存在很多误解,这些错误会(不必要地)使这个主题更加混乱和复杂。

答案 4 :(得分:26)

闭包是一个可以引用另一个函数中的状态的函数。例如,在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

答案 5 :(得分:23)

为了帮助理解闭包,检查它们如何以过程语言实现可能是有用的。这个解释将遵循Scheme中闭包的简单实现。

首先,我必须介绍命名空间的概念。当您向Scheme解释器输入命令时,它必须评估表达式中的各种符号并获取它们的值。例如:

(define x 3)

(define y 4)

(+ x y) returns 7

define表达式将值3存储在x的点中,将值4存储在y的点中。然后当我们调用(+ x y)时,解释器在命名空间中查找值并能够执行操作并返回7.

但是,在Scheme中,有一些表达式允许您临时覆盖符号的值。这是一个例子:

(define x 3)

(define y 4)

(let ((x 5))
   (+ x y)) returns 9

x returns 3

let关键字的作用是引入一个新的命名空间,其中x为值5.您会注意到它仍然能够看到y是4,使得返回的总和为9.您还可以看到一次表达式已经结束x回到了3.在这个意义上,x已被本地值暂时屏蔽。

程序和面向对象的语言有类似的概念。无论何时在函数中声明一个与全局变量同名的变量,都会产生相同的效果。

我们如何实现这一目标?一种简单的方法是使用链表 - 头部包含新值,尾部包含旧命名空间。当你需要查找一个符号时,你从头部开始沿着尾部向下工作。

现在让我们跳到一流函数的实现。或多或少,函数是一组在函数被调用时返回值最终执行的指令。当我们读入一个函数时,我们可以在幕后存储这些指令,并在调用函数时运行它们。

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns ?

我们将x定义为3,将plus-x定义为其参数y,加上x的值。最后,我们在一个x被新x掩盖的环境中调用plus-x,这个值为5.如果我们只是为函数plus-x存储操作(+ xy),因为我们在上下文中如果x为5,则返回的结果为9.这就是所谓的动态范围。

然而,Scheme,Common Lisp和许多其他语言都有所谓的词法作用域 - 除了存储操作(+ x y)之外,我们还将命名空间存储在该特定点。这样,当我们查找值时,我们可以看到x,在这个上下文中,实际上是3.这是一个闭包。

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns 7

总之,我们可以使用链表来存储函数定义时命名空间的状态,允许我们从封闭范围访问变量,并使我们能够在不影响变量的情况下本地屏蔽变量。其余的计划。

答案 6 :(得分:10)

这是一个真实世界的例子,说明为什么Closures会屁股...这是我的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秒钟。好吧delay很简单,关闭:

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

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

如果没有闭包,你必须以某种方式将这些变量状态保持在函数外部,从而在函数外乱丢代码,逻辑上属于它。使用闭包可以大大提高代码的质量和可读性。

答案 7 :(得分:6)

TL;博士

闭包是一个函数,其范围分配给(或用作变量)。因此,名称闭包:范围和函数被封闭和使用,就像任何其他实体一样。

深入维基百科风格解释

According to Wikipedia, a closure是:

  

在具有第一类函数的语言中实现词法范围名称绑定的技术。

这是什么意思?让我们看看一些定义。

我将使用此示例解释闭包和其他相关定义:

function startAt(x) {
    return function (y) {
        return x + y;
    }
}

var closure1 = startAt(1);
var closure2 = startAt(5);

console.log(closure1(3)); // 4 (x == 1, y == 3)
console.log(closure2(3)); // 8 (x == 5, y == 3)

一流的功能

基本上这意味着我们可以像使用任何其他实体一样使用函数。我们可以修改它们,将它们作为参数传递,从函数返回它们或为变量分配它们。从技术上讲,它们是first-class citizens,因此得名:一流的功能。

在上面的示例中,startAt返回一个(anonymous)函数,该函数被分配给closure1closure2。因此,正如您所看到的,JavaScript就像其他实体(一等公民)一样对待函数。

名称绑定

Name binding是关于找出变量(标识符)引用的数据。范围在这里非常重要,因为这将决定如何解决绑定。

在上面的示例中:

  • 在内部匿名函数的范围内,y绑定到3
  • startAt的范围内,x绑定到15(取决于结束)。

在匿名函数范围内,x未绑定任何值,因此需要在上限(startAt)范围内解析。

词汇范围

作为Wikipedia says,范围:

  

绑定有效的计算机程序区域:其中名称可用于引用实体

有两种技巧:

  • 词法(静态)范围:变量的定义通过搜索其包含的块或函数来解析,然后如果搜索外部包含块失败,依此类推。
  • 动态范围:搜索调用函数,然后调用调用函数的函数,依此类推,调用堆栈。

有关详细说明,请check out this questiontake a look at Wikipedia

在上面的示例中,我们可以看到JavaScript是词法范围的,因为当解析x时,将根据源代码在上层(startAt)范围内搜索绑定(查找x的匿名函数在startAt内定义)并且不基于调用堆栈,调用函数的方式(范围)。

包裹(关闭)

在我们的示例中,当我们调用startAt时,它将返回一个(第一类)函数,该函数将分配给closure1closure2,从而创建一个闭包,因为传递的变量15将保存在startAt的范围内,该范围将包含在返回的匿名函数中。当我们通过closure1closure2使用相同的参数(3)调用此匿名函数时,将立即找到y的值(因为这是该参数)函数),但是x没有绑定在匿名函数的范围内,因此解析在(词法)上部函数作用域(保存在闭包中)继续,其中x被发现是绑定到15。现在我们知道了总和的所有内容,因此可以返回结果,然后打印出来。

现在您应该了解闭包及其行为方式,这是JavaScript的基本组成部分。

柯里

哦,你还学到了currying的含义:你使用函数(闭包)来传递一个操作的每个参数,而不是使用一个带有多个参数的函数。

答案 8 :(得分:5)

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

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

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

答案 9 :(得分:4)

在正常情况下,变量受范围规则约束:局部变量仅在定义的函数内有效。关闭是暂时违反此规则的一种方式。

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会破坏正常的范围规则,并在内部开始使用。

foo.call(3)

返回12.

答案 10 :(得分:2)

简而言之,函数指针只是指向程序代码库中某个位置的指针(如程序计数器)。而 Closure =函数指针+堆栈帧

答案 11 :(得分:1)

闭包是有状态的函数,由另一个函数返回。即使父函数已完成执行,它还是一个容器,可从其父范围记住变量和参数。考虑这个简单的例子。

  function sayHello() {
  const greeting = "Hello World";

  return function() { // anonymous function/nameless function
    console.log(greeting)
  }
}


const hello = sayHello(); // hello holds the returned function
hello(); // -> Hello World

看!我们有一个返回函数的函数!返回的函数将保存到变量中并调用下面的行。

答案 12 :(得分:1)

•闭包是一个子程序,并且 引用环境 定义

–如果子程序需要引用环境 可以从程序中的任意位置调用

–不允许嵌套的静态范围语言 子程序不需要关闭

–仅当子程序可以访问时才需要关闭 嵌套作用域中的变量,可以从 任何地方

–为支持闭包,实现可能需要 提供一些变量的无限范围(因为 子程序可以访问非本地变量,即 通常不再活着

示例

function makeAdder(x) {
return function(y) {return x + y;}
}
var add10 = makeAdder(10);
var add5 = makeAdder(5);
document.write(″add 10 to 20: ″ + add10(20) +
″<br />″);
document.write(″add 5 to 20: ″ + add5(20) +
″<br />″);

答案 13 :(得分:1)

关闭是JavaScript中的一项功能,其中一个函数可以访问自己的作用域变量,访问外部函数变量和全局变量。

即使外部函数返回后,闭包也可以访问其外部函数范围。这意味着闭包即使在函数完成之后也可以记住并访问其外部函数的变量和参数。

内部函数可以访问在其自身范围,外部函数的范围和全局范围中定义的变量。并且外部函数可以访问在其自身范围和全局范围中定义的变量。

******************
Example of Closure
******************

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,这是其内部函数自变量,外部函数变量和全局变量值的总和。

答案 14 :(得分:0)

闭包 每当我们在另一个函数中定义一个函数时,内部函数就可以访问声明的变量 在外部功能。最好通过示例解释闭包。 在清单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.)-Beginning%20Node.js-Apress%20(2014).pdf

答案 15 :(得分:0)

这是一个说明 Scheme 编程语言中的闭包的示例。

首先我们定义一个定义局部变量的函数,在函数外不可见。

  const Stack = createStackNavigator();
  const AppSwitchNavigator = () => {
    return (
      <NavigationContainer>
        <Stack.Navigator  initialRouteName="WelcomeScreen">
          <Stack.Screen name="TabsBottom" component={TabsBottom} options={{ headerShown: false }} />
          <Stack.Screen name="WelcomeScreen" component={WelcomeScreen} />
          <Stack.Screen name="Register" component={Register} />
          <Stack.Screen name="RegisterTwo" component={RegisterTwo} />
          <Stack.Screen name="Login" component={Login} />
        </Stack.Navigator>
      </NavigationContainer>
    );
  }

  export const AppContainer = AppSwitchNavigator;

这是同一个例子,但现在函数使用了一个在函数外部定义的全局变量。

; 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

答案 16 :(得分:0)

Groovy中的一个简单示例供您参考:

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

答案 17 :(得分:0)

关闭为JavaScript提供了状态。

编程中的状态只是意味着记住事情。

示例

var a = 0;

a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3

在上述情况下,状态存储在变量“ a”中。接下来,我们将“ a”加1数次。我们只能这样做是因为我们能够“记住”价值。状态持有者“ a”将该值保存在内存中。

通常,您需要使用编程语言来跟踪事物,记住信息并在以后访问。

其他语言通常是通过使用类来完成的。与变量一样,类也跟踪其状态。而该类的实例又在其中具有状态。状态只是表示您以后可以存储和检索的信息。

示例

class Bread {
  constructor (weight) {
    this.weight = weight;
  }

  render () {
    return `My weight is ${this.weight}!`;
  }
}

我们如何从“渲染”方法中访问“权重”?好吧,感谢国家。面包类的每个实例都可以通过从“状态”中读取重量来呈现自己的重量,“状态”是我们可以存储该信息的位置。

现在, JavaScript是一种非常独特的语言,它过去没有类(现在有类),但是在幕后只有函数和变量),因此闭包为JavaScript提供了一种记住事物并以后再访问它们。

示例

var n = 0;
var count = function () {
  n = n + 1;
  return n;
};

count(); // # 1
count(); // # 2
count(); // # 3

以上示例通过变量实现了“保持状态”的目标。这很棒!但是,这样做的缺点是现在公开了变量(“状态”持有人)。我们可以做得更好。我们可以使用闭包。

示例

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

这太棒了。

现在我们的“计数”功能可以计数了。它之所以只能这样做是因为它可以“保持”状态。在这种情况下,状态为变量“ n”。现在已关闭此变量。时空封闭。因为您将无法及时恢复,更改,为其分配值或直接与它进行交互,所以及时了。在太空中,因为它在地理上嵌套在“ countGenerator”函数中。

为什么这很棒?因为我们无需涉及任何其他复杂的复杂工具(例如类,方法,实例等), 1.隐藏 2.远距离控制

我们隐藏了状态“ n”,这使它成为私有变量! 我们还创建了一个可以以预定义方式控制此变量的API。特别是,我们可以像这样调用API:“ count()”,然后将“距离”加1到“ n”。除非通过API,否则任何人都不能以任何形式访问“ n”。

JavaScript的简单性确实令人赞叹。

关闭是原因所在。

答案 18 :(得分:0)

关闭非常容易。我们可以考虑如下: 闭包=函数+其词法环境

考虑以下功能:

function init() {
    var name = “Mozilla”;
}

以上情况下的关闭将是什么? 在其词法环境(即名称)中使用init()函数和变量。 关闭 = init()+名称

考虑其他功能:

function init() {
    var name = “Mozilla”;
    function displayName(){
        alert(name);
}
displayName();
}

这里的封包是什么? 内部函数可以访问外部函数的变量。 displayName()可以访问在父函数init()中声明的变量名。但是,如果存在,则将使用displayName()中相同的局部变量。

关闭1: init函数+(名称变量+ displayName()函数)->词法范围

关闭2: displayName函数+(名称变量)->词法范围

答案 19 :(得分:0)

Currying:它允许您仅传递函数的子集来部分评估函数。考虑一下:

function multiply (x, y) {
  return x * y;
}

const double = multiply.bind(null, 2);

const eight = double(4);

eight == 8;

闭包:闭包无非就是访问函数范围之外的变量。重要的是要记住,函数内部或嵌套函数不是闭包。当需要访问函数范围之外的变量时,总是使用闭包。

function apple(x){
   function google(y,z) {
    console.log(x*y);
   }
   google(7,2);
}

apple(3);

// the answer here will be 21

答案 20 :(得分:0)

请查看以下代码,以更深入地了解闭包:

\

这里将输出什么? const placeholder = ( <Typing> <span>This span will get typed.</span> </Typing> ); <input type="text" placeholder={placeholder} /> 不会因为关闭而成为 for(var i=0; i< 5; i++){ setTimeout(function(){ console.log(i); }, 1000); }

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

0,1,2,3,4

让我简单地解释一下,当一个函数创建完之后,直到它在第一个代码中调用了for循环才发生了5次,但是没有立即被调用,所以当它调用即1秒钟后又是异步的,因此在此for循环结束之前并没有被调用将值5存储在var i中,最后执行五次5,5,5,5,5函数并打印 for(var i=0; i< 5; i++){ (function(j){ //using IIFE setTimeout(function(){ console.log(j); },1000); })(i); }

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

setTimeout

有关更多信息,请了解执行上下文以了解闭包。

  • 还有一种使用let(ES6功能)解决此问题的解决方案,但在上述功能的作用下可以正常工作

    5,5,5,5,5

=>更多说明:

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

循环1)

       (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

循环2)

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

Output: 0,1,2,3,4

循环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(function(){ console.log(i); },1000); 中执行了五次时,它将打印{{1} }

因此要解决此问题,请使用上面说明的IIFE。

答案 21 :(得分:0)

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

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

函数g是一个闭包:g关闭a。因此g可以与成员函数进行比较,a可以与一个类字段,以及一个带有类的函数f

答案 22 :(得分:0)

来自Lua.org

  

当函数被包含在另一个函数中时,它可以完全访问封闭函数中的局部变量;此功能称为词法范围。虽然这听起来很明显,但事实并非如此。词法范围,加上一流的功能,是编程语言中一个强大的概念,但很少有语言支持这一概念。

答案 23 :(得分:0)

这是另一个现实生活中的例子,并使用在游戏中流行的脚本语言 - 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'。

相关问题