(JS)闭包是否需要函数内部的函数

时间:2011-08-17 17:20:47

标签: javascript closures

我对封闭的固有概念有点困难。我得到了基本的想法,但事情就是这样:我认为,从技术上讲,每个Javascript函数中都有“闭包”。引用维基百科:

  

在计算机科学中,一个闭包(也是词法闭包,功能闭合   或函数值)是一个函数和引用   该函数的非局部名称(自由变量)的环境。   据说这样的函数是“关闭”它的自由变量。

因此,既然您可以在函数内部定义变量,那么它们将“关闭”到代码的其余部分,因此我将其视为闭包。因此,据我所知:

(function(){var a = 1;}())

是一个(不是非常有用)闭包的例子。或者哎呀,即使只是这样:

function(){var a = 1;}

但是,我认为我的理解可能是错误的。其他人告诉我,对于某些东西来说,它必须坚持一个状态,所以因为除了代码之外没有任何东西持续存在,所以它并不是真正的封闭。这表明你需要:

function(foo){foo.a = 1;}(bar); // bar.a = 1

或甚至(以确保不可修改性):

function(foo){var a = 1; bar.baz = function() { return a}}(bar); // bar.baz() = 1

因此,从技术上讲(我知道其中几个例子实际上毫无意义,但是)上述哪些例子实际上是闭包的例子。并且闭包只需要是一个空间(即在JS函数内部),其中可以存储无法从外部访问的变量,或者持久性是闭包定义的关键部分吗?

修改

刚刚注意到Stack Overflow上的“closures”标签的维基定义:

  

闭包是一个引用(关闭)的第一类函数   来自定义范围的变量。如果关闭仍然   在其定义范围结束后存在,它关闭的变量   也将继续存在。

虽然SO wiki当然不是最终权威,但第一句似乎与我对该术语的理解相关。然后第二句建议如何使用闭包,但它似乎似乎就像一个要求。

编辑#2

如果从这里不一致的答案,维基百科答案和标签答案中不清楚,似乎没有就“封闭”这个词的含义达成一致意见。所以虽然我很欣赏到目前为止的所有答案,但如果你按照作者对闭包的定义,它们都是有意义的,我想我真正想要的是......是否有任何实际的“权威”定义(然后如果是的话,它如何适用于上述所有内容?)

3 个答案:

答案 0 :(得分:9)

你被错误的假设“封闭”这个词的来源误导了。

在语言理论上下文中,闭包的意思是函数可以引用在外面它自己定义的变量。它是否具有内部变量,或者内部变量是否从外部不可见是无关紧要的。换句话说,它是关于从函数到其定义环境中看到 out ,而不是从函数外部看到 in

为什么这个奇怪的词呢?查看上一个示例中的函数:

bar.baz = function() { return a }

此函数包含对函数体本身未定义的变量a的提及。它是函数体的“自由”变量,在定义中是一个“洞”。如果不通过一些无关的方法知道正文中的标识符a所指的变量,我们就无法执行该函数。在运行时形成一个闭包,将这个“开放”函数体与对应的变量相对应,从而关闭定义中的孔。这就是名字的来源。

(如果你想要完全技术性的解释,那么潜在的概念就是lambda-calculus中一个“封闭”的术语,这意味着没有自由变量。只有封闭的术语具有独立的含义。 (通常编译的)非封闭源代码的组合,以及使其行为类似于封闭术语的上下文信息,因此可执行。)

附录:在常见的习语中

function() {
   var blah;
   // some code here
}();

要点 来获得一个闭包(当然,你会得到一个,但它不会对你做任何有趣的事情),但要创建一个本地范围代表blah变量。本地范围在概念上与闭包是完全不同的 - 实际上除了Javascript之外的大多数C-lookalike都会在每个{}块创建它们,而它们可能有也可能没有闭包。

答案 1 :(得分:2)

从技术上讲,你的样品都不是封闭的。 (但在某些情况下,可以将样本分类为这样,见下文)

Closure是一种数据结构,它结合了对函数的引用和声明时激活的调用帧(或作用域)的非空列表。

通过执行一些包含使用外部作用域变量的函数声明的代码来创建闭包。在这种情况下,运行时,在执行代码时,不仅要创建对函数的引用,还要创建闭包结构 - 函数引用和对当前环境的引用 - 保存使用的外部变量的调用框列表。

例如,在我的TIScript中,调用帧被替换为堆栈 - 当您从函数中退出时,其包含的变量集合的调用框架将从堆栈中清除。在我的情况下创建闭包发生在:VM遇到函数声明指令并且该函数被标记(通过编译器)作为使用外部变量的函数。在这种情况下,保存已使用变量的当前调用帧链从堆栈移动到堆 - 转换为GCable数据对象,并引用函数将其调用链存储为引用。

你的第四种情况在物理上不需要创建闭包 - 不需要存储调用帧供以后使用 - bar.baz只包含一个数字 - 而不是对函数的引用。

但是这个:

function(foo){  
   var a = 1; 
   bar.baz = function() { return a; }; 
}

bar.baz字段中创建闭包。当您稍后调用bar.baz()时,将执行函数代码,并且将从引用存储在闭包中的外部调用帧中获取“a”变量的值。

希望它为你清除一些东西。

答案 2 :(得分:0)

JavaScript(和其他语言)中的闭包用于控制和定义范围。不要求在函数内定义函数以使其“限定”为闭包。函数体是一个闭包。其中一个更常见的用途是声明一个局部范围变量,该变量将成为您将返回的某个其他对象或函数的私有或隐藏成员,但这不是一个硬性规则。

相关问题