锁定老帖子 主题:我对Javascript闭包的理解
精华帖 (1) :: 良好帖 (12) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-12-07
最后修改:2011-12-21
我对Javascript闭包的理解
关于闭包是什么,这个问题困扰了我很久。我好像知道是什么,但好想又说不清楚。今天,我查阅了相关的资料,把自己的一些理解记录在此,以备他日有用。 “闭包”这个词,我不知道是谁翻译的,我个人感觉,这个翻译真的是误导了不少中国程序员。 本人呢,也曾经幼稚的以为“闭包”就是“Close package”。对比了《Javascript权威指南》中文和英文版本,我才知道我们所说的闭包,人家洋大哥叫做“Function Scope and Closures”(这个见《Javascript Definitive Guide》第八章8.8节)。《JavaScript: The Good Parts》(《Javascript语言精粹》)第四章 4.10 叫做Closures。 从上面的描述,我想大家已经知道闭包的字面意思,“一个封闭的函数作用域”。此话怎讲?就是说在一个闭包内声明的变量,只有在这个闭包内定义的函数才能访问。 说了这么多,不知道看的人迷糊没,反正我是迷糊了。迷糊了咋办,写个例子理解一下。 我们有一个函数,名字比方说叫做tmd(虽然这个名字不好听,但是我想不出一个更合适的了,凑合用吧)。这个函数是干什么用的呢?你想干什么就干什么。 function tmd(){ //在这儿你可以随便写你想些的代码,我无所谓。 }
现在有个想法,就是我想知道tmd这个函数被调用了多少次? 这活好干啊,声明个全部变量tmdNum,每次调用tmd的时候+1不就搞定了。 var tmdNum = 0; function tmd(){ //在这儿你可以随便写你想些的代码,我无所谓。 tmdNum ++; alert(tmdNum); } 现在很好,代码运行正常,也能完成我们的需求。
话说在一个阳光明媚的下午,本人正在喝着咖啡和MM聊天呢,同事告诉我说tmd这个方法呗调用了3000多次。本人听完脑袋都大了,谁这么脑残也不能在一个页面里一个方法调3000多次啊。经过一段痛苦的查找,在我那个亲爱的搭档的代码里面发现了这么一句: tmdNum = 3000; 看来全局变量是不能用了,太不安全了。怎么办,用闭包。
var tmd = function(){//这里我们叫做"匿名函数1" var tmdNum = 0; return function(){//这里我们叫做"匿名函数2” //在这儿你可以随便写你想些的代码,我无所谓。 tmdNum ++; alert(tmdNum); }
}(); //千万要注意这里的这个小括号。 这段代码什么意思呢? 第一步:先把函数里面的内容都删了,看总体的结构 var tmd = function(){}(); 这行代码的意思是 先声明一个变量,变量的名字叫做tmd。 function(){} 这是声明了一个匿名函数 function(){}(); 加个括号的意思是让这个匿名函数立即执行。 所以,tmd的值就是这个匿名函数的返回值。如果这个函数什么也不返回,tmd的值就是undefined。 第二步:看匿名函数里面的内容 匿名函数里面有做了两件事情 一是定义了一个变量tmdNum,并赋初值为0; 二是执行一个return,return function(){} 表示返回的是一个函数。这个函数的函数体执行的步骤跟我们文章一开始写的那个tmd函数的执行步骤是一样的。 结果:我们折腾了这么一大圈,达到的目的是什么呢。首先,匿名函数1 return了一个函数(匿名函数2)赋给了tmd变量。这样调用tmd(),实际调用的就是匿名函数2。因为tmdNum这个变量只能被匿名函数2调用,所以最终的效果就是tmdNum这个变量只能在tmd()调用的时候改变。 从此,互不干涉,天下太平。(以上故事纯属扯淡,扯扯更健康!)
补充点资料
这里我摘抄了《Javascript设计模式》里面的一段话,补充下关于闭包的问题: 闭包(closure)是一个受到保护的变量空间,由内嵌函数生成。 Javascript具有函数级别的作用域。这意味着定义在函数内的变量不能在函数外部被访问。 Javascript的作用域又是词法性质的(lexically scoped)。这意味着函数运行在定义它的作用域中,而不是调用它的作用域中。把这两个因素结合起来,就能通过把变量包裹在匿名函数中而对其加以保护。 这里要注意的一点是: Javascript的作用域是函数范围的,在Java等语言中 public void fun(){ int i = 3; if(i > 0){ int b = 0; }else{ int b = 1; } //System.out.println(b); //Java语言里面,在这里b是不能访问的。因为b的作用域在大括号内。 }
Javascript中,情况不一样: function fun(){ var i = 3; if(i > 0){ var b = 0; }else{ var b = 1; } alert(b); //Javascript中,这里访问b是没有问题的,因为Javascript中,作用域是函数范围内的。 }
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-12-07
牛人啊...
|
|
返回顶楼 | |
发表时间:2011-12-08
说得挺明白的 非常感谢 不知道楼下有没有什么补充的
|
|
返回顶楼 | |
发表时间:2011-12-08
哎,这里的人气真的是不怎么样啊。
|
|
返回顶楼 | |
发表时间:2011-12-09
通俗易懂 不错
|
|
返回顶楼 | |
发表时间:2011-12-09
最后修改:2011-12-09
把iteye搞残的闭包
http://www.iteye.com/topic/1118236 另外给个我写的闭包非闭包代码对比 http://happysoul.iteye.com/blog/1280900 |
|
返回顶楼 | |
发表时间:2011-12-09
闭包这词儿可能最早来源于离散数学,然后就被用在了编程上面
|
|
返回顶楼 | |
发表时间:2011-12-09
例子挺有意思的,其实大多数闭包使用的场景好像都如楼主所示,主要是js的变量作用域范围和一般语言不太一样,再要深入说就是作用域链和活动对象(activation object)的关系了
|
|
返回顶楼 | |
发表时间:2011-12-09
最后修改:2011-12-09
这个写的挺有意思
很多框架里都这么写 你这个例子更加通俗易懂 |
|
返回顶楼 | |
发表时间:2011-12-09
貌似最后的语句里开始的“var tmd = ”可以不写,用不到。
|
|
返回顶楼 | |