昨天晚上上了网易前端微专业的第一次直播课,感觉非常好,老师也普及了一个之前面试经常会被问到的问题——闭包。并且老师在一开始提出了一个近乎颠覆之前认知的一个结论:闭包不会造成内存泄漏。
相信这个结论也颠覆了很多人的一个认知,那么我们接下来就来看看网易的老师是怎么解释这个问题的吧。
闭包的概念最早出现于60年代,最早实现闭包的程序语言是[Scheme]。之后,闭包被广泛使用于[函数式编程]。
实际上,闭包是一种带有执行环境的函数。执行环境包括了:函数的词法环境(this、作用域)、标识符列表(用到但是未声明的变量)、表达式部分(函数体)。
下面举一个JS中闭包的例子:
function fn(){
var a = 2;
return function (){
console.log(a) //2;
}
}
fn()();
在上面的例子中我们可以看到,在内部函数中,我们并没有声明变量a,可是输出结果却是2。这个a就属于用到但未声明的变量,这也就是闭包与一般函数最大的区别所在。
那么闭包到底会不会造成内存泄漏呢?
我们先来看维基百科里对于内存泄漏的定义:
在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。
上面这段定义用通俗点的方法讲就是:内存泄漏就是一些可以避免的内存花销。
那么我们再回过头看上面的例子,变量a是有实际用途的,因此这样的内存开销是不可避免的,因此这样的形式并不叫内存泄漏。
那么是什么原因会造成闭包会出现内存泄漏这种说法的呢?下面这张图给出了答案:
由于IE的垃圾回收机制采用的是引用计数策略,所以才会有闭包会造成内存泄漏的说法。
引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。
而诸如Chrome、Firefox等现代浏览器的垃圾回收机制是标记清除式的垃圾回收算法。
标记清除:垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记,然后,它会去掉环境中的变量的标记和被环境中的变量引用的变量的标记,此后,如果变量再被标记则表示此变量准备被删除。
至此我们应该已经明白了,JS中内存泄漏的真正的罪魁祸首,也理解了闭包和内存泄漏的概念。希望下次再被问到闭包相关的问题的时候,我们不再不知所措。
参考资料: