JavaScript 闭包的一个包罗万象的定义

One all-encompassing definition of JavaScript closure

我阅读了 10 篇关于闭包的 SO 参考、MDN 参考和其他博客文章。他们似乎都以自己的方式定义闭包。例如,来自 MDN 文档:

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

这是他们对闭包的解释:

Normally, the local variables within a function only exist for the duration of that function's execution. Once makeFunc() has finished executing, it is reasonable to expect that the name variable will no longer be accessible. Since the code still works as expected, this is obviously not the case.

The solution to this puzzle is that myFunc has become a closure. A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created. In this case, myFunc is a closure that incorporates both the displayName function and the "Mozilla" string that existed when the closure was created.

下面的 Whosebug post 将闭包作为可见范围的堆栈来回答。 What types of scope exist in Javascript?

我困惑的地方:闭包是一个对象吗?或者它只是一种“异常范围情况”,其中内部嵌套函数可以访问在其自身外部定义但对容器父函数而言是本地的变量,即使在父函数已经执行之后也是如此?闭包是指像 myFunc 这样的嵌套函数(范围)情况的对象还是内部函数本身?

简而言之,

The function inside another function, has the access to the variables declared in the outer function. In case the function is in the global context, it obviously has the access to the global variables.

更多上下文:

var v1; // I'm accessible anywhere    
function a() {
    var v2;
    function b() { // I can access v2 and v1
        var v3;
        function c() {  // I can access v1, v2, v3
            var v4;
        }
        return c;
    }
    return b();
}
var f = a();

在上面,abc 都是闭包,它们可以访问它们各自的父范围,并且递归地进行到 window 或全局上下文。


一般来说,每个函数都是一个闭包。但是我们只是想到它们,当我们实现某些东西时,实际上 依赖于闭包,例如工厂函数。

过去很多课程 material 和参考文献都强调 JavaScript 的 OO 方面,有时会忽略功能方法。我认为随着框架的开发和 JS 库的广泛收集,这种情况开始发生变化。 Secrets of the JavaScript Ninja 说明掌握函数是有效使用 JavaScript 的基本部分。在Ninja中闭包的定义更为通用:

"... closures allow a function to access all the variables, as well as other functions, that are in scope when the function itself is declared."

                                         -- "Chapter 5: Closing in on closures"

讨论闭包,Effective JavaScript(或多或少)这样说:

  • 函数可以引用在其范围之外定义的变量;
  • 函数可以在第二个函数返回后引用另一个函数定义的变量(因为在 JavaScript 中函数是对象);
  • 函数可以包含“封闭”的内部函数,这些函数可以修改封闭函数的属性。

Effective JavaScript 第 2 章第 11 项“熟悉闭包”的源代码位于 gibhub 上:

封闭函数的巧妙之处在于,如果您不打算显式调用它,则它不需要名称:它可以是匿名的,只需作为外部函数的一部分进行计算。

你可以把JS函数看成这样的结构:

class Function : Object {
  bytes      bytecode;
  varframe   varsAndArgs;
}

class varframe{
  array<value>  values;
  ptr<varframe> parent;
}

所以JS中的每个函数实例在技术上都是一个闭包。 在父指针为空的顶层函数中。

所以当你定义

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

displayName (const/variable) 将包含 class 函数的实例,该函数将包含对此结构的自身 varframe 的引用:

varframe(displayName) 
  values[0] // empty array, no variables in it
  parent -> varframe(makeFunc) 
              values[1] // one variable "name" at index 0;
              parent = null    

因此闭包是一个结构,包含对代码的引用和对 varframes 链的引用 (a.k.a.callframes)。