JavaScript 闭包概念

在定时器、事件监听器、 Ajax 请求、跨窗口通信、Web Workers 或者任何其它的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包

参考书籍

《JavaScript权威指南》

词法作用域(lexical scoping)的执行依赖于变量作用域,这个作用域是在函数 定义时 决定的,而不是函数调用时。为了实现这种词法作用域,JavaScript 函数对象的内部状态不仅包含函数的代码逻辑,还必须引用当前的作用域链。函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为「闭包」。

从技术角度讲,所有的 JavaScript 函数都是闭包:它们都是对象,它们都关联到作用域链。当调用函数时闭包所指向的作用域链和定义函数时的作用域链不是同一个作用域链时,事情就变得非常微妙。当一个函数嵌套了另外一个函数,外部函数将嵌套的函数对象作为返回值返回的时候,这种事情就发生了。

JavaScript 也采用词法作用域(lexical scoping),也就是说,函数的执行依赖于变量作用域,这个作用域是在函数定义时决定的,而不是函数调用时决定的。为了实现这种词法作用域,JavaScript 函数对象的内部状态不仅包含函数的代码逻辑,还必须引用当前的作用域链。函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为“闭包”。

从技术角度讲,所有的 JavaScript 函数都是闭包:它们都是对象,它们都关联到作用域链。 如果这个函数定义了嵌套的函数,并将它作为返回值返回或者存储在某处的属性里,这时就会有一个外部引用指向这个嵌套的函数。它就不会被当做垃圾回收,并且它所指向的变量绑定对象也不会被当做垃圾回收。

引用

《你不知道的JavsScript》

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包。

无论何时何地,如果将函数(访问它们各自的词法作用域)当作第一级的值类型并到处传递,你就会看到闭包在这些函数中的应用。在定时器、事件监听器、 Ajax 请求、跨窗口通信、Web Workers 或者任何其它的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包。

从表面上来看,闭包是一种具有状态的函数。或者也可以将闭包的特征理解为,其相关的局部变量在函数调用结束之后将会继续存在。

《JavaScript编程解析》

闭包仅仅是保持了变量名查找的状态,而并没有保持对象所有的状态,对此请加以区分。也就是说,闭包虽然会保持(在嵌套外层进行函数调用时被隐式地生成的)Call 对象,但无法保持 Call 对象的属性所引用的之前的对象的状态。

总结

JavaScript 闭包概念关键点:

  • 当函数是在当前词法作用域之外执行,可以记住并访问所在的内部词法作用域时,就产生了闭包
  • JavaScript 执行作用域是在函数 定义时 决定的,而不是函数调用时
  • 回调函数是闭包的常用使用体现
  • 闭包仅仅是保持了变量名查找的状态,而并没有保持对象所有的状态
晓月风尘 wechat
扫描二维码与我相识
你我共同创造价值,记得支持一下哦~