日期:2014-05-16 浏览次数:20372 次
如果前端人员不懂Javascript闭包,那只能说他压根就没懂Javascript,只能算入门级。本篇主要是写本人对闭包的一些理解,欢迎拍板。
?
先引用一下官方解释:
?
?
用我的蹩脚英语翻译过来换成自己的理解就是:
?
?
可能上边解释还是有点抽象,那我拿实际的代码举个例子:
var name = "window"; function outer(){ var name = 'outer'; function inner(){ return name; } return inner; } alert(outer()());
?
可以试着在浏览器运行一下这段script,会发现,弹出框内容为outer。
我先解释这个过程大致发生了什么,再联系前面说的两点说说我对闭包的理解。
首先第一行定义了name变量,很容易理解,name就是属于当前的浏览的页面吧!(实际name就是在当前window底下的全局变量,至于window是什么变量,不懂的话就把它当做是当前页面所在的环境吧)。
第2行-第12行我们定义了outer函数。
再看看第13行发生了什么,outer()()这是什么语法???那我们先分解来看吧,outer()一般是干嘛呢?当然是执行outer函数然后返回一个值咯。那在这里返回值是什么呢?inner!好,那就是说outer() == inner,应该同意吧?
于是乎outer()() == inner(),
所以最后alert出来的值将会是inner函数返回的值。
?
乱了乱了,不是说过,在一对括号{}里算是一个域,在这个域里边定义的东西,在离开这个域之后就会被销毁吗?
上面这个观点是对的,但是这个就是javascript闭包强大的地方:它把本来要销毁的一些变量给挽救回来,继续放在内存里边,让后边可以调用这个闭包来获取这些变量。
?
回到例子上吧,我们在outer函数里边定义了inner函数,如果说你最后不把它返回并且复制给变量,这个inner函数就会被销毁了,但是我们在第13行将它抛出去给外界执行了!!!于是js解释器知道它被暴露在外头了,不应该被销毁,所以就留住它的所在的环境,也就是outer的这个域。里边的name也被留下来了。
?
执行outer()之后,我们获取了刚刚说被暴露在外边的inner函数了,紧接着执行inner(),于是inner就开始找寻name变量了,我们知道inner所在的环境被嵌套了2层,inner->outer->window,当然啦,按照我们就近原则,我们肯定现在inner里边找name变量,发现找不到,于是到outer那个环境去找,此时找到了name,把name返回给inner()调用处alert出来。(这里可以思考一下脚本找寻变量的过程,就可以知道过多地访问全局变量会经常要把时间消耗在搜索过程,所以尽量避免过多地访问全局变量,至于怎么做,比较简单的就是采用一个局部变量记录该全局变量并替换成访问该局部变量)
?
我简单的把上述例子跟前面的解释联系起来:
1. 闭包首先是一个表达式,通常这个表达式是一个函数。
? ? 例子中的inner函数就是这么一个表达式。
2. 闭包拥有一个环境,使得它能够拥有这个环境底下的这个许多变量。
??? inner函数返回并提供给外界调用导致了inner会继续停留在内存,从而提供了一个环境可以访问outer里边的变量。
?
按我理解,可以把闭包以及闭包的用法简单总结成以下:
1.闭包一般就是函数,并且定义在一个函数里边的。当然了,闭包还可以是一个对象的。如下代码:
function outer(){ var name = 'outer'; return { inner:function(){ return name; } }; } alert(outer().inner());
2.在函数里边定义函数不一定是闭包,而是应该将这个函数返回并且赋值给外界或者给外界执行这个闭包。
因为js解释器要知道到底要不要销毁域底下的东西,如果它发现外界持有这个闭包的引用,它就不会去销毁这个环境。
3.闭包允许外界访问函数里边的局部变量,例如outer里边的name变量。
4.运用闭包可以储存函数里边的变量,可以避免更多的全局变量定义而污染,我觉得可以简单的跟面向对象的封装进行类比。
5.闭包会导致变量跟一些引用停留在内存不被销毁,将会导致内存消耗。
?
对于Javascript,我认为它的闭包是其精华所在,使它变得更加灵活以及衍生了很多高级功能。关于闭包,网上有很多精彩的讲解,这里只是记录一下本人的理解,欢迎交流。阮一峰的学习Javascript闭包(Closure) 对闭包的讲解很不错,推荐阅读一下。