自从在一本js书上看到闭包的概念后,我就一直在网上寻找关于闭包的文章,今天终于有点眉目了。
先看一下闭包的概念所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数)。当然,这个概念我也是从其他地方拷来的,我的理解是:闭包其实和定义一个类有点相像。类把一些数据封装起来,并且说明哪些成员是私有的,哪些成员是公有的,闭包也有类似的作用。看一下这个例子:
其中函数closures形成了一个闭包,只有getNum函数能引用num而外部环境不能引用。此时num像一个私有成员,getNum函数是一个公共方法。如果我们再调用一次fun函数:
虽然函数closures已执行完毕,然而其内部的变量num却没有被销毁,这是由于getNum函数的执行依赖于num变量,这是js内部的一种机制,由于其过于复杂,在这里就不说了。让我们再看一个例子:
为什么全是3呢?联系一下我上面的比喻很容易理解.我们把变量i看做是closures的一个私有变量,foo看做是一个公共方法列,每次调用foo[j],函数foo[j]就从closures里面找i的值然后把它输出,然而在我们每次调用foo[j]时,i的值都是循环结尾的3,并不是foo[j]创建的i的值,从而解释了为什么都是3.相信知道了这个,可以让我们避免很多莫名其妙的问题.比如:
也是3次都是3,真可气的3啊。这又是怎么了呢?根据我的理解,当调用setTimeout函数时就好像是在window上绑定了一个时间触发事件,定时发生。我们回头看一下代码。回调函数是一个匿名函数,并且还引用了全局变量i。结合前面所说的,当回调函数执行时,循环早已结束,i的值已经是3,并且固定下来,此时如果引用i自然只能得到我们所不想要的了。那怎么办呢?方法还是有的:
终于成功了。让我们分析一下代码,首先解释一下(function(){})(),这是一个自执行的匿名函数,也就是定义完了立即执行。在这个自执行函数里面我们定义了一个局部变量j,关键就在这个"j"(注意:我们共有3个"j"),在每一次循环过程中,我们都新建了一个匿名,然后用局部变量j记录了此时i的值,当timer事件激发时,alert()向上找j的值,并且每次找到的j值都不相同,因为他们分属不同的闭包(每个自执行函数都是一个闭包).至此我们的问题得到了解决。
以上纯属个人观点,能力有限,难免有错误的地方,希望大家来指正。
以下是关于闭包的其他文章:
一篇不错的文章,通俗易懂:http://blog.morrisjohns.com/javascript_closures_for_dummies.html
想深入了解的话,这篇文章可以满足你,不过要有心理准备,来自javaeye的文章:http://xiasheng.javaeye.com/blog/344405