闭包

127 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

导言

闭包,翻译自英文单词closure。这个概念第一次出现在 1964 年的《The Computer Journal》上,由 P. J. Landin 在《The mechanical evaluation of expressions》一文中提出了 applicative expression 和 closure的概念。如果仅从字面意思来看,根本理解不了。我们再来看看它在计算机领域的一些定义:

  • 编译原理中,它是处理语法产生式的一个步骤
  • 计算几何中,它表示包裹平面点集的凸多边形
  • 在编程语言领域,它表示一种函数

表示一种函数好像能知道一点,但表示哪种函数呢?不清楚。

闭包自从它的出现,就存在各种各样的定义,但这些对于我们开发者理解闭包基本毫无用处。

更通俗的解释

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

如果以第一句当函数携带它所在的词法作用域时就产生了闭包为标准,那最普通的函数也产生了闭包,因为它携带了全局作用域,这样理解闭包就没有什么大意义。那再进一步,函数不在全局作用域中,而是定义在另一个函数内部,如下:

function foo () {
  var a = 1;
  function innerFun () {
    console.log(a);
  }
  innerFun();
}
foo(); // 输出1

这是闭包吗?从技术角度来说,也许算,因为内部函数innerFun携带了它所在的词法作用域。但同样对我们真正理解什么是闭包意义不大。闭包的核心是在上文闭包解释的最后一句:即使函数是在当前词法作用域之外执行。这句话才能真正帮助我们理解闭包。我们将上面代码稍作修改:

function foo () {
  var a = 1;
  function innerFun () {
    console.log(a);
  }
  return innerFun
}
var baz = foo();
baz(); // 输出1

也是输出1。两个例子不同之处在于:本例中把内部函数innerFun的引用作为返回值返回了,而在它词法作用域之外去执行,仍然能够访问它的词法作用域,例如a的值。是不是有点理解了:在foo函数执行后,其函数作用域并没有得到释放,其引用而是被返回赋值给了baz,即是闭包。正常来说,内部能访问外部变量或属性,反之则不行,而闭包却实现了,这正是闭包存在的价值。只是这是这样的话,会使得部分内存不能释放,在使用闭包过程中需要注意,防止内存泄露问题的发生。