闭包是如何工作的?

简单来说,闭包(closure)是函数在声明时访问函数自身之外的变量时所创建的作用域

让我们将这句话分解来看:一、闭包是作用域,二、函数在声明时访问外部变量所创建的作用域。另外:声明的函数在后续的任何时刻都可以被调用,即使是声明时的作用域消失之后。

从最简单的代码开始理解:

在这个最简单的例子中,outerFunction能够访问到外部的outerValue变量。这样的代码,我们可能已经编写了成千上万次,但是却没有意识到其实我们已经创建了一个闭包。

一点都不特别是吧?因为外部变量与外部函数都是在全局作用域内声明的,该作用域(也是闭包)从未消失过(因为页面已经被加载)。所以该函数可以访问到外部变量也就不足为奇了,因为它仍在作用域内并且是可用的。这种情况下,即使有闭包的存在,我们也不清楚他的好处。

接下来让我们看看另外一个例子:

运行结果:
closure_1

有没有什么不对劲的地方?在outerFunction执行时,我们通过将innerFunction的赋值全局变量later。当innerFunction执行时,outerFunction的作用域早已不复存在了,并且在通过later调用innerFunction的那个时间点上也不可见了。按理来说第二个 log 方法应该输出undefined

但运行结果却给出了相反的答案,为什么?是什么原因让innerValue变量在later执行的时候仍然“活着”?而且还是在outerFunction的作用域消失之后?答案当然是闭包。

outerFunction中声明innerFunction的时候,不仅声明了函数,还创建了一个闭包,该闭包不仅包含函数声明,还包含了函数声明的那一时间点上该作用域的所有变量

innerfunction-cosure
闭包像泡泡一样保护其中的变量

最终当innerFunction执行的时候,虽然outerFunction的作用域已经消失了。但是通过闭包,还是能够访问到innerValue变量的。

闭包就像一层保护膜,只要innterFunction函数一直存在,闭包就保护该作用域内的变量不被垃圾回收。针对在函数声明那一时间点的作用域内的所有函数和变量,闭包创建了一个“安全气泡”,因此函数获得了执行操作所需要的所有东西,包含了函数及其变量,和函数本身在一起。

再看一个例子,来观察一下闭包的一些核心原则:

运行结果:
what_else_closure_can_see

运行结果说明了三个关于闭包更有趣的概念:

  1. 内部函数的参数是包含在闭包中的。
  2. 作用域之外的所有变量,即使是函数声明之后的那些变量,也都包含在闭包中。
  3. 相同的作用域中,未声明的变量不能提前引用

第二点和第三点解释为什么内部闭包可以访问到变量tooLate,而外部闭包不行。

最后,闭包这种方式在存储和引用信息方面会直接影响性能。重要的是要记住,每个闭包进行信息访问的函数都有一个作用域链,如果我们愿意,可以在它上面附加任何信息,便性能开销随之增加。

发表评论

电子邮件地址不会被公开。 必填项已用*标注