详谈JavaScript 匿名函数及闭包.docx
- 文档编号:26723482
- 上传时间:2023-06-22
- 格式:DOCX
- 页数:11
- 大小:18.72KB
详谈JavaScript 匿名函数及闭包.docx
《详谈JavaScript 匿名函数及闭包.docx》由会员分享,可在线阅读,更多相关《详谈JavaScript 匿名函数及闭包.docx(11页珍藏版)》请在冰豆网上搜索。
详谈JavaScript匿名函数及闭包
详谈JavaScript匿名函数及闭包
1、匿名函数
函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途。
匿名函数:
就是没有函数名的函数。
1.1函数的定义,首先简单介绍一下函数的定义,大致可分为三种方式
第一种:
这也是最常规的一种
代码如下:
functiondouble(x){
return2*x;
}
第二种:
这种方法使用了Function构造函数,把参数列表和函数体都作为字符串,很不方便,不建议使用。
代码如下:
vardouble=newFunction('x','return2*x;');
第三种:
vardouble=function(x){return2*x;}
注意“=”右边的函数就是一个匿名函数,创造完毕函数后,又将该函数赋给了变量square。
1.2匿名函数的创建
第一种方式:
就是上面所讲的定义square函数,这也是最常用的方式之一。
第二种方式:
代码如下:
(function(x,y){
alert(x+y);
})(2,3);
这里创建了一个匿名函数(在第一个括号内),第二个括号用于调用该匿名函数,并传入参数。
2、闭包
闭包的英文单词是closure,这是JavaScript中非常重要的一部分知识,因为使用闭包可以大大减少我们的代码量,使我们的代码看上去更加清晰等等,总之功能十分强大。
闭包的含义:
闭包说白了就是函数的嵌套,内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕(这点涉及JavaScript作用域链)。
示例一
代码如下:
functioncheckClosure(){
varstr='rain-man';
setTimeout(
function(){alert(str);}//这是一个匿名函数
2000);
}
checkClosure();
这个例子看上去十分的简单,仔细分析下它的执行过程还是有许多知识点的:
checkClosure函数的执行是瞬间的(也许用时只是0.00001毫秒),在checkClosure的函数体内创建了一个变量str,在checkClosure执行完毕之后str并没有被释放,这是因为setTimeout内的匿名函数存在这对str的引用。
待到2秒后函数体内的匿名函数被执行完毕,str才被释放。
示例二,优化代码
代码如下:
functionforTimeout(x,y){
alert(x+y);
}
functiondelay(x,y,time){
setTimeout('forTimeout('+x+','+y+')',time);
}
/**
*上面的delay函数十分难以阅读,也不容易编写,但如果使用闭包就可以让代码更加清晰
*functiondelay(x,y,time){
*setTimeout(
*function(){
*forTimeout(x,y)
*}
*,time);
*}
*/
3、举例
匿名函数最大的用途是创建闭包(这是JavaScript语言的特性之一),并且还可以构建命名空间,以减少全局变量的使用。
示例三:
代码如下:
varoEvent={};
(function(){
varaddEvent=function(){/*代码的实现省略了*/};
functionremoveEvent(){}
oEvent.addEvent=addEvent;
oEvent.removeEvent=removeEvent;
})();
在这段代码中函数addEvent和removeEvent都是局部变量,但我们可以通过全局变量oEvent使用它,这就大大减少了全局变量的使用,增强了网页的安全性。
我们要想使用此段代码:
oEvent.addEvent(document.getElementById('box'),'click',function(){});
示例四:
代码如下:
varrainman=(function(x,y){
returnx+y;
})(2,3);
/**
*也可以写成下面的形式,因为第一个括号只是帮助我们阅读,但是不推荐使用下面这种书写格式。
*varrainman=function(x,y){
*returnx+y;
*}(2,3);
*/
在这里我们创建了一个变量rainman,并通过直接调用匿名函数初始化为5,这种小技巧有时十分实用。
示例五:
代码如下:
varouter=null;
(function(){
varone=1;
functioninner(){
one+=1;
alert(one);
}
outer=inner;
})();
outer();//2
outer();//3
outer();//4
这段代码中的变量one是一个局部变量(因为它被定义在一个函数之内),因此外部是不可以访问的。
但是这里我们创建了inner函数,inner函数是可以访问变量one的;又将全局变量outer引用了inner,所以三次调用outer会弹出递增的结果。
4、注意
4.1闭包允许内层函数引用父函数中的变量,但是该变量是最终值
示例六:
代码如下:
/**
*
*
- one
- two
- three
- one
*
*
*
*
*
*/
varlists=document.getElementsByTagName('li');
for(vari=0,len=lists.length;i lists[i].onmouseover=function(){ alert(i); }; } 你会发现当鼠标移过每一个 这是为什么呢? 注意事项里已经讲了(最终值)。 显然这种解释过于简单,当mouseover事件调用监听函数时,首先在匿名函数(function(){alert(i);})内部查找是否定义了i,结果是没有定义;因此它会向上查找,查找结果是已经定义了,并且i的值是4(循环后的i值);所以,最终每次弹出的都是4。 解决方法一: 代码如下: varlists=document.getElementsByTagName('li'); for(vari=0,len=lists.length;i (function(index){ lists[index].onmouseover=function(){ alert(index); }; })(i); } 解决方法二: 代码如下: varlists=document.getElementsByTagName('li'); for(vari=0,len=lists.length;i lists[i].$$index=i;//通过在Dom元素上绑定$$index属性记录下标 lists[i].onmouseover=function(){ alert(this.$$index); }; } 解决方法三: 代码如下: functioneventListener(list,index){ list.onmouseover=function(){ alert(index); }; } varlists=document.getElementsByTagName('li'); for(vari=0,len=lists.length;i eventListener(lists[i],i); } 4.2内存泄露 使用闭包十分容易造成浏览器的内存泄露,严重情况下会是浏览器挂死。 可以参考下面内容预防: JavaScript垃圾回收机制 JavaScript不需要手动地释放内存,它使用一种自动垃圾回收机制(garbagecollection)。 当一个对象无用的时候,即程序中无变量引用这个对象时,就会从内存中释放掉这个变量。 复制代码代码如下: vars=[1,2,3]; vars=null; //这样原始的数组[1,2,3]就会被释放掉了。 3、循环引用 三个对象A、B、C AàBàC: A的某一属性引用着B,同样C也被B的属性引用着。 如果将A清除,那么B、C也被释放。 AàBàCàB: 这里增加了C的某一属性引用B对象,如果这是清除A,那么B、C不会被释放,因为B和C之间产生了循环引用。 复制代码代码如下: vara={}; a.pro={a: 100}; a.pro.pro={b: 100}; a=null; //这种情况下,{a: 100}和{b: 100}就同时也被释放了。 varobj={}; obj.pro={a: 100}; obj.pro.pro={b: 200}; vartwo=obj.pro.pro; obj=null; //这种情况下{b: 200}不会被释放掉,而{a: 100}被释放了。 4、循环引用和闭包 复制代码代码如下: functionouter(){ varobj={}; functioninner(){ //这里引用了obj对象 } obj.inner=inner; } 这是一种及其隐蔽的循环引用,。 当调用一次outer时,就会在其内部创建obj和inner两个对象,obj的inner属性引用了inner;同样inner也引用了obj,这是因为obj仍然在innerFun的封闭环境中,准确的讲这是由于JavaScript特有的“作用域链”。 因此,闭包非常容易创建循环引用,幸运的是JavaScript能够很好的处理这种循环引用。 5、IE中的内存泄漏 IE中的内存泄漏有好几种,这里有详细的解释( 这里只讨论其中一种,即循环引用所造成的内存泄漏,因为,这是一种最普遍的情况。 当在DOM元素或一个ActiveX对象与普通JavaScript对象之间存在循环引用时,IE在释放这类变量时存在特殊的困难,最好手动切断循环引用,这个bug在IE7中已经被修复了(http: //www.quirksmode.org/blog/archives/2006/04/ie_7_and_javasc.html)。 “IE6sufferedfrommemoryleakswhenacircularreferencebetweenseveralobjects,amongwhichatleastoneDOMnode,wascreated.ThisproblemhasbeensolvedinIE7.” 如果上面的例子(第4点)中obj引用的不是一个JavaScriptFunction对象(inner),而是一个ActiveX对象或Dom元素,这样在IE中所形成的循环引用无法得到释放。 复制代码代码如下: functioninit(){ varelem=document.getElementByid('id'); elem.onclick=function(){ alert('rain-man'); //这里引用了elem元素 }; } Elem引用了它的click事件的监听函数,同样该函数通过其作用域链也引用回了elem元素。 这样在IE中即使离开当前页面也不会释放这些循环引用。 6、解决方法 基本的方法就是手动清除这种循环引用,下面一个十分简单的例子,实际应用时可以自己构建一个addEvent()函数,并且在window的unload事件上对所有事件绑定进行清除。 复制代码代码如下: functionouter(){ varone=document.getElementById('one'); one.onclick=function(){}; } window.onunload=function(){ varone=document.getElementById('one'); one.onclick=null; }; 其它方法(by: DouglasCrockford) 复制代码代码如下: /** *遍历某一元素节点及其所有后代元素 * *@paramElemnode 所要清除的元素节点 *@paramfunctionfunc 进行处理的函数 * */ functionwalkTheDOM(node,func){ func(node); node=node.firstChild; while(node){ walkTheDOM(node,func); node=node.nextSibling; } } /** *清除dom节点的所有引用,防止内存泄露 * *@paramElemnode 所要清除的元素节点 * */ functionpurgeEventHandlers(node){ walkTheDOM(node,function(e){ for(varnine){ if(typeofe[n]=== 'function'){ e[n]=null; } } }); 以上就是JavaScript内存泄漏的相关内容以及解决方案了,有需要的小伙伴可以参考下
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 详谈JavaScript 匿名函数及闭包 详谈 JavaScript 匿名 函数