我花了好长的时间,看了网上好多的博客才理解了(自以为)闭包的意义。
我分以下几点来讲闭包:
-
俯(藐)视闭包----简单玩意儿(方法论),你可以选择看完就走
-
变量的一生----变量的销毁和生存时间
-
匿名函数----不是小透明的小透明
-
闭包陷阱----万用的例子
-
审视闭包----它的作用,整个JS的函数世界都是闭包?
-
细看闭包----我不看这节!(欢迎跳过,但有闲情可以来看看,书画怡情)
-
避免闭包----内存占用,或者不想用(任性)!,你真的用不到吗?有时候是这样的
1.俯视闭包:
假装写代码:
过去,我在写代码,我是用二进制写的,我只知道0和1;
如今,我在写代码,我用的是记事本,我知道abcd;
未来,我在写代码,也许我根本不用学JS???一呼一吸就是法则?
闭包定义:
闭包是指有权访问另一个函数作用域中的变量的函数。
翻译:在今天,我可以不知道函数内部是怎么样运行的,它们已经被封装在了函数里,被语法所定义了,我只要知道toString()就是返回字符串就行了。这其实就是闭包。
我只要知道我能通过某种方式获取信息就行了。这种方式就是闭包。
function 科技发展(){ //现代版
var 内部编译原理=xxx;
return function 使用方法(莫须有的参数){
通过xxx,你想做什么都可以;
}
}
var 我要使用科技=科技发展();
我要使用科技(莫须有的参数);
//就这样,我可以不懂(无法直接获取)xxx,但我懂使用(获取)方法就可以了
学过C++或者相似语言的还可发现,这跟类的私有变量很像对吧。我们可以用接口函数获取,但不能直接获取。
(1)闭包是一种设计原则,它通过分析上下文,来简化用户的调用,让用户在不知晓的情况下,达到他的目的;
(2)网上主流的对闭包剖析的文章实际上是和闭包原则反向而驰的,如果需要知道闭包细节才能用好的话,这个闭包是设计失败的;
(3)尽量少学习。 -------http://kb.cnblogs.com/page/110782/
其实,我们可以从两方面的角度看闭包。
----------a.从设计者的角度上,用户没法直接获得数据,其实也就是实现了数据的私有。
----------b.从使用者的角度上,用户可以通过另外的方式(这个方式也许更加简单、方 便,具体根据设计者来定)获得数据或者甚至操作封装好了的数据。
END.
想继续学请看下方。
2.变量的一生:
- 在JS世界里面,有这样的清洁原则,环境中无法访问到的变量就被清除。就像一个被所有人遗忘的人那样,所有人都觉得他死了。
- JS里面有两种变量,一种是全局变量,一种是局部变量。局部变量是个依附着所属空间的东西,一旦所属空间没有,它也就灰飞烟灭了。而全局变量呢,它的生命周期就是js文件使用的周期。
所以如果你要销毁变量,如果是全局变量,那么你需要覆盖它,如果是局部变量你只要等待它所在的方法结束,它就自动销毁了。
function test(){var a=3;}
alert(a); //error,a 已经被销毁了
for(var a=3;a<10;a++){}
alert(a); // 输出10,因为a在全局中定义,还没有被销毁
- 但闭包是一种特殊的情况。闭包中的变量,这个函数的空间将会一直在内存中保留。
function test(){ var a=3; return function(){ return a; } } b=test();
虽然在外部没法输出a,这是因为没法访问,但a还是存在于内存之中。因为内部的函数引用了外部的变量a(引用计数法垃圾清除,为0则删除),所以a还被人惦记着,自然也不会消亡。
3.匿名函数:
- 什么是非匿名函数呢?
function I_have_name(){//do something}
//它就是有名字的函数,它的名字叫做 "I_have_name"
function (){//do something}
//它是匿名函数,它没有定义名字
- 什么又是自执行函数呢?
(function (){
alert(3);
})(); //它就是自执行函数
(function auto(){
alert(3);
})(); //它也是自执行函数,效果同上
//也就是说那个auto没有卵用?(此句是版主自以为的)
//--------------------------------------------------
//此处还有一个坑,那就是如果
function(){
alert(3);
}(); //这样写是错的!!!!!!!!!!!!!!
为什么(function {// code})();可以被执行, 而function {// code}();却会报错?
答:
1.我们来看他们的区别,其实前者它叫函数声明,而后者它叫函数表达式。
2.JS的预编译的时候我先看函数声明,忽略函数表达式。就像给声明开了个后门一样。
3.当JS正式编译的时候,声明过的函数看到了(function{//code}就跳过了,因为它编译过了,所以,他只会看到()这个东西。???这个是什么玩意儿,于是函数就报错了。
4.而匿名函数还是完整的,所以能够正常执行。
5.还需要知道的是,自执行函数的标识可以是
!function(){}() (function(){})()
~function(){}() void function(){}()
6.自执行函数是可以带参数的,格式是这样的!
function(num){
alert(num);
}(3); 输出3
4.闭包陷阱
然后我们来说一个常见的闭包例子,这是一个闭包的副作用。
var arr=[1,2,3];
var obj={};
var test=function(){
for(var i=0;i<arr.length;i++)
{
obj[i]=function(){
alert(i);
}
}
}
test();
for(var i=0;i<arr.length;i++)
{
obj[i](); //最后的输出是输出了三个3.
}
按照正常的思维,最后应该是obj[0]=0,obj[1]=1,obj[2]=2,但是真正的过程是这样的:
- 在obj[i]的函数域里面,因为要输出i,而它自己的空间里,是没有i的,于是它就往外层找,在外层找到了i,而外层的这个i呢,它是被保存在外围的函数里面的。
- 接下来是关键,外层的这个函数是一个对象对吧(所有的函数都是一个对象),而我们访问的正是这个对象的i,而在这个对象中保存的i其实是变量的地址,最终它是以10保存的。
- 所以,我们的解决办法就是,跟obj[i]里面的i一样,创造一个两个函数之间沟通的机会,实现即时的按值传递。
var test=function(){
for(var i=0;i<arr.length;i++)
{
obj[i]=function(canshu){
alert(canshu);
}(i);
}
}
test();
for(var i=0;i<arr.length;i++)
{
obj[i](); //最后分别输出:0,1,2 达成预期
}
如果你看懂了上面的,你就明白了。
对!我使用了匿名函数!一个带参数的匿名函数。这里的(i),是在外围的区域里面的,跟obj[i]中i一模一样,而作为内部函数里,它是被赋值(或者我们还可以说是复制)给canshu,所以,它是即时按值传递的!!!
(PS:自己发明的词语,仅供参考,主要的问题在于,我不知道底层到底是如何运行的,说它是引用又觉得不太对劲)
这个过程的实现也成功避免了闭包带来的陷阱。
5. 审视闭包:
其实我们甚至可以说,在全局环境中写的function,都是一个闭包。
参考文献
1.超级推荐的文章
2.什么是闭包,闭包的优缺点
3.Javascript闭包——懂不懂由你,反正我是懂了
4.浅析匿名函数