首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >什么是“闭包”?

什么是“闭包”?
EN

Stack Overflow用户
提问于 2008-08-31 04:38:21
回答 23查看 161.7K关注 0票数 501

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

EN

回答 23

Stack Overflow用户

回答已采纳

发布于 2011-09-19 05:04:53

变量作用域

当您声明一个局部变量时,该变量有一个作用域。通常,局部变量只存在于声明它们的块或函数中。

代码语言:javascript
复制
function() {
  var a = 1;
  console.log(a); // works
}    
console.log(a); // fails

如果我试图访问一个局部变量,大多数语言都会在当前作用域中查找它,然后向上遍历父作用域,直到它们到达根作用域。

代码语言:javascript
复制
var a = 1;
function() {
  console.log(a); // works
}    
console.log(a); // works

当一个块或函数结束时,它的局部变量就不再需要了,并且通常会耗尽内存。

这就是我们通常期望的工作方式。

闭包是一个持久的局部变量作用域

闭包是一个持久化的作用域,即使在代码执行移出该块之后,它也会保留局部变量。支持闭包的语言(如JavaScript、Swift和Ruby)将允许您保留对某个作用域(包括其父作用域)的引用,即使在声明这些变量的块完成执行之后也是如此,前提是您在某个地方保留了对该块或函数的引用。

作用域对象及其所有局部变量都绑定到该函数,只要该函数继续存在,它们就会一直存在。

这为我们提供了函数的可移植性。我们可以预期,当第一次定义函数时,任何在作用域中的变量在以后调用该函数时仍然在作用域中,即使我们在完全不同的上下文中调用该函数。

例如

下面是一个用JavaScript编写的非常简单的示例,它说明了这一点:

代码语言: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退出后仍然存在,那么在定义时,所有在作用域中的变量也将持久化。变量a已被关闭--它在一个闭包内。

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

您可能会猜到,当我调用fnc()时,它会输出a的值,即"1“。

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

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

a属于outer的范围。inner的作用域有一个指向outer作用域的父指针。fnc是一个指向inner的变量。只要fnc存在,a就会一直存在。a在闭包中。

进一步阅读(观看)

我用一些实际的用法示例查看了这段代码,并制作了一个YouTube video

票数 862
EN

Stack Overflow用户

发布于 2008-08-31 04:49:37

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

代码语言: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提供任何参数,所以它必须以某种方式记住计数。它知道在哪里根据所谓的词法作用域来查找它-它必须查找它定义的位置来查找值。这个“隐藏”值就是所谓的闭包。

下面是我再次使用的例子:

代码语言:javascript
复制
function add (a) {
  return function (b) {
    return a + b;
  }
}

var add3 = add(3);
    
add3(4); returns 7

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

票数 114
EN

Stack Overflow用户

发布于 2016-04-27 10:33:56

首先,与这里大多数人告诉你的相反,闭包是函数闭包而不是函数闭包( a !)那么这是什么呢?

它是在函数的“周围上下文”(称为其环境)中定义的一组符号,这些符号使其成为封闭表达式(即,在该表达式中,每个符号都定义了一个值,因此可以对其求值)。

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

代码语言:javascript
复制
function closed(x) {
  return x + 3;
}

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

但是如果你有一个像这样的函数:

代码语言:javascript
复制
function open(x) {
  return x*y + 3;
}

它是一个开放表达式,因为其中有尚未定义的符号。也就是说,y。当我们看这个函数时,我们不知道y是什么,它的意思是什么,我们不知道它的值,所以我们不能计算这个表达式。也就是说,我们不能调用这个函数,直到我们告诉它y应该是什么意思。这个y被称为自由变量。

这个y需要一个定义,但这个定义不是函数的一部分-它是在其他地方定义的,在它的“周围环境”(也称为环境)中。至少这是我们所希望的:

例如,可以全局定义它:

代码语言:javascript
复制
var y = 7;

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

或者可以在包装它的函数中定义它:

代码语言:javascript
复制
var global = 2;

function wrapper(y) {
  var w = "unused";

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

环境中赋予表达式中自由变量含义的部分是闭包。它被称为这种方式,因为它通过为所有自由变量提供这些缺失的定义,将开放表达式转换为封闭表达式,以便我们可以对其进行计算。

在上面的例子中,内部函数(我们没有给它命名,因为我们不需要它)是一个开放表达式,因为其中的变量y是自由的-它的定义在函数外部,在包装它的函数中。该匿名函数的环境是变量集:

代码语言:javascript
复制
{
  global: 2,
  w: "unused",
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

现在,闭包是这个环境的一部分,它通过提供所有自由变量的定义来关闭内部函数。在我们的例子中,内部函数中唯一的自由变量是y,所以该函数的闭包是它的环境的这个子集:

代码语言:javascript
复制
{
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

环境中定义的其他两个符号不是该函数闭包的一部分,因为它不需要它们运行。不需要它们来关闭它。

更多关于这背后的理论:https://stackoverflow.com/a/36878651/434562

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

这就是为什么人们经常混淆术语闭包是一种特殊类型的函数,它可以对他们使用的外部变量进行快照,或者是用于存储这些变量的数据结构。但我希望你现在明白,它们不是闭包本身-它们只是在编程语言中实现闭包的方法,或者是允许来自函数闭包的变量在需要时存在的语言机制。关于闭包有很多误解,这(不必要地)使这个主题比实际情况更令人困惑和复杂。

票数 73
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36636

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档