前端从业者在日常工作中都会接触到闭包这个概念,但不管是初入前端的新手,还是资深老鸟对应闭包概念能解释清楚的寥寥无几。
这篇文章我们用杂谈的形式带着你重新理解下什么是闭包。
起源:
闭包翻译自英文单词 closure,这个概念第一次出现在 1964 年的《The Computer Journal》上,由 P. J. Landin 在《The mechanical evaluation of expressions》一文中提出了closure 的概念。
在上世纪 60 年代,主流的编程语言是基于 lambda 演算的函数式编程语言,所以这个最初的闭包定义,使用的函数式术语,一个不太精确的描述是"带有一系列信息的λ表达式"。对函数式语言而言,λ表达式其实就是函数。
对应我们现有的知识储备可以这样理解,闭包其实只是一个绑定了执行环境的函数,他与普通函数的区别是,它携带了执行的环境。
在此闭包是什么,我们就已经学会,并且也跟大家阐述了一个很重要的观点:闭包不会造成内存泄露。闭包只是一个绑定了执行环境的函数仅此而已。
但如果文章到此结束,我想你已经指的电脑准备骂娘了,阅读文章不仅没有解决疑惑,反而疑惑更多了。
例如:
- 闭包会造成内存泄露的说法怎么流传的?
- 什么是执行环境?
接下来我一一给大家解释。
闭包会造成内存泄露的说法怎么流传的?
先理解内存泄露
百度百科:内存泄漏是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
js程序员:内存泄露是指你用不到(访问不到)的变量,依然占居着内存空间,不能被再次利用起来。
回归到主要矛盾上,为什么会流传闭包会导致内存泄露!因为IE浏览器早期的垃圾回收机制,有 bug。
- IE浏览器中使用完闭包之后,依然回收不了闭包里面引用的变量。
- 在IE浏览器中,由于BOM和DOM中的对象是使用C++以COM对象的方式实现的,而COM对象的垃圾收集机制采用的是引用计数策略。在基于引用计数策略的垃圾回收机制中,如果两个对象之间形成了循环引用,那么这两个对象都无法被回收,但循环引用造成的内存泄露在本质上也不是闭包造成的。
如上可看出都是万恶 IE 的问题,不是闭包的问题,这也是流传闭包会导致内存泄露的主要原因。
什么是执行环境?
讲这部分内容之前起立,感谢下程劭非(winter)前手机淘宝前端负责人,之前拜读过他这部分文章,对我重新梳理有很大帮助。
在早期古典的闭包定义中,闭包包含两个部分。
- 环境部分
- 表达式部分
当我们把目光放在 JavaScript 的标准中,其实并没有出现过 closure 这个术语,但是根据古典定义,在 JavaScript 中找到对应的闭包组成部分。
环境部分:
- 环境:函数的词法环境(执行上下文)
- 标识符列表:函数中用到的未声明的变量
表达式部分:函数体
这里很容易产生一个常见的概念误区,有些人会把 JavaScript 执行上下文,或者作用域(Scope,ES3 中规定的执行上下文的一部分)这个概念当作闭包。
实际上 JavaScript 中跟闭包对应的概念就是"函数",可能是这个概念太过于普通,跟闭包看起来又没什么联系,所以大家才不自觉地把这个概念对应到了看起来更特别的"作用域"。
最后再次感谢: 程劭非(winter)对于执行环境这部分内容做的梳理总结。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。