\

闭包16 May 2014

tech js

今天和大家探讨一下闭包概念和我对闭包的理解。

##前言

昨天面试的时候,一不小心就聊到了javascript上面来了,笔者让面试者聊一下对闭包的理解。结果面试者把问题抛回来了,问我什么是闭包,突然被反问,措手不及中,还真有点卡壳。于是整理一下我对闭包的理解。

##正题

说到闭包,我们先来聊一下离散数学中的闭包概念

数学中,对一个集合的成员进行某种运算,生成的仍然是这个集合的成员,则该集合被称为在某个运算下闭合。`

再来看一下计算机科学里的闭包概念

在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。`

笔者个人认为,无论在数学还是计算机中,所谓闭包,无非是一个闭合区间。这个闭合区间内的所有元素,具有相同的生命周期。

闭包存在于匿名函数中,所有支持匿名函数的编程语言都有闭包的概念(javascript,java,c#)。

具体到javascript中,类似于下面这种形式,会产生闭包

function checkClosure(){
    var str = 'huangchaosuper';
    setTimeout(
        function(){ alert(str); } //这是一个匿名函数
    , 2000);
}
checkClosure();

之所以在javascript中常常提闭包,是因为以下几个条件在javascript中经常被同时满足。

  • javascript中普遍喜欢异步编程风格
  • javascript支持匿名函数
  • 大家为了省事,基本都用匿名回调函数
  • 大家为了更省事,大家都直接在匿名函数中使用外部函数的变量

于是带来了一个生命周期的问题。

仍然拿上面的代码做例子

function(){ alert(str); }

此函数在执行的时候str并没有在内部定义,换句话说,var str = 'huangchaosuper';与function具有相同的生命周期。

再具体一点说,function checkClosure()函数执行结束之后,并没有马上把资源回收而是等待function(){ alert(str); }执行结束后,一起回收。那再加一层呢?没错,所有的层级一起回收。可能有的读者意识到了这个问题:循环引用A>B>C>B。是的,B和C由于产生了循环引用,这部分内存永远不会被释放。

这样我们引出了另外一个问题,当function checkClosure()非常占用内存空间,但是执行比较快,并且其匿名函数执行时间很久虽然占用内存空间很小。

这种情况下会给大家一个checkClosure已经被回收的错觉,看到这里,大家应该懂我的意思了吧。

##结论

使用闭包十分容易造成浏览器的内存泄露,严重情况下会是浏览器挂死。随着Nodejs的兴起,我相信不久的将来,基于异步编程风格的NodeJS在web应用中将会一统天下。随着云计算的兴起,对于单线程的这个问题,反而成为了Nodejs编程简单的优势。最大的问题在于,如何让不知不觉中使用闭包的初级开发者应付内存泄漏。

comments powered by Disqus