闭包的应用场景

转载 2016年08月30日 16:39:01

闭包的应用场景

[Don't use closures unless you really need closure  semantics.]不要使用闭包,除非你真正需要它。

[In most cases, non-nested functions are the right way to go.]请使用无嵌套函数。

闭包的应用场景一

闭包的应用场景二

闭包的应用场景三

-------------------------------------------

什么是闭包?

复制代码
function a(){ var i=0; function b(){ alert(i); } return b; } var c = a(); c();
复制代码

全局变量c指定对 函数a的内部函数b的引用;内部函数b的执行需要依赖函数a的资源;

这里就产生一个闭包,使得a在执行完毕并返回后,不会被javascript垃圾回收机制GC回收。

因为这里c还在引用着b,而b依赖着a,故a在使用后,仍然存在于内存中。

简而言之:当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。

 

闭包的应用场景

  1.使用闭包代替全局变量

  2.函数外或在其他函数中访问某一函数内部的参数

  3.在函数执行之前为要执行的函数提供具体参数

  4.在函数执行之前为函数提供只有在函数执行或引用时才能知道的具体参数

     5.为节点循环绑定click事件,在事件函数中使用当次循环的值或节点,而不是最后一次循环的值或节点

     6.暂停执行

     7.包装相关功能

1.使用闭包代替全局变量

   全局变量有变量污染和变量安全等问题。

复制代码
//全局变量,test1是全局变量 var test1=111 function outer(){ alert(test1); } outer(); //111 alert(test1); //111
 
 
//闭包,test2是局部变量,这是闭包的目的 //我们经常在小范围使用全局变量,这个时候就可以使用闭包来代替。 (function(){ var test2=222; function outer(){ alert(test2); } function test(){ alert("测试闭包:"+test2); } outer(); //222 test(); //测试闭包:222 } )(); alert(test2); //未定义,这里就访问不到test2
复制代码

  

2.函数外或在其他函数中访问某一函数内部的参数

   为了解决在Ajax callback回调函数中经常需要继续使用主调函数的某一些参数。

复制代码
function f1(){ var test=111; tmp_test=function(){return test;} //tmp_test是全局变量,这里对test的引用,产生闭包 } function f2(){ alert("测试一:"+tmp_test()); var test1=tmp_test(); alert("测试二:"+test1); } f1(); f2(); //测试一:111 //测试二:111 alert(tmp_test()); //111 tmp_test=null;
复制代码

  

3.在函数执行之前为要执行的函数提供具体参数

某些情况下,是无法为要执行的函数提供参数,只能在函数执行之前,提前提供参数。

有哪些情况是延迟执行?

如:setTimeOut 

     setInterval

     Ajax callbacks

     event handler[el.onclick=func 、 el.attachEvent("onclick",func)]

复制代码
//无法传参的情况 var parm=222; function f1(){alert(111)} function f2(obj){alert(obj)} setTimeout(f1,500);//正确,无参数 var test1=f2(parm);//执行一次f2函数 setTimeout(f2,500);//undefined,传参失败 setTimeout(f2(parm),500);//参数无效,传参失败 setTimeout(function(parm){alert(parm)},500);//undefined,传参失败 document.getElementById("hello").onclick=f1;//正确 document.getElementById("hello").attachEvent("onclick",f1);//正确
  //正确做法,使用闭包 function f3(obj){return function(){alert(obj)}} var test2=f3(parm);//返回f3的内部函数的引用 setTimeout(test2,500);//正确,222 document.getElementById("hello").onclick=test2;//正确,222 document.getElementById("hello").attachEvent("onclick",test2);//正确,222
复制代码

4.在函数执行之前为函数提供只有在函数执行或引用时才能知道的具体参数 

复制代码
//动态绑定a集合的注册点击事件,在事件处理函数test1中提供参数-该点击事件的a元素本身。 var aa="ha!" function test(obj){return function(){alert(obj);}} var nodes=document.getElementsByTagName("a"); for(var i=0;i<nodes.length;i++){ var test1=test(aa);//由于是提前提供参数,只能提供已知的具体参数,无法事先得知点击事件的a元素本身。 //这里想提供点击事件的a元素本身作为参数宣告失败! nodes[i].onclick=test1;//只有在注册点击事件时,才会知道该点击事件的a元素是哪个 }
  //以下是解决方式 function associateObjWithEvent(obj,methodName){ return (function(e){ e=e||window.event; return obj[methodName](e,this);//重点看这里!有两个参数,
        //e:event,元素绑定事件时,绑定的是对内部函数的引用,故在触发事件时,执行的是内部函数。
        //内部函数有个e参数,刚好在事件触发时,能捕获到是什么事件类型。
        //this:这里需要的就是this参数,以便当元素触发事件处理函数执行时,this=触发事件的元素本身
        //this参数无法从外部传入进来。传入进来的this都会被转化特定对象    
    );
} 

function DhtmlObject(elId){
    var el=document.getElementById(elId);
    if(el){
        //el.onclick=associateObjWithEvent(this,"doOnClick");
        el.onmouseover=associateObjWithEvent(this,"doMouseOver");
        el.onmouseout=associateObjWithEvent(this,"doMouseOut");
    }
}
DhtmlObject.prototype.doMouseOver=function(event,element){
    alert(event);//第一个参数,只在事件执行时,才知道是什么事件,这里是MouseEvent
    alert(arguments[0]);//第一参数,
   alert(element);//第二个参数,只在事件执行时,才知道是指代触发事件的元素本身
    alert(arguments[1]);//第二个参数
}

var hello=new DhtmlObject("hello");  //执行
复制代码

另一个例子

复制代码
function associateObjWithEvent(obj,methodName){ return (function(e){ e=e||window.event; return obj[methodName](e); }); } function DragListener(){ this.down=function(){ alert(this) alert(arguments[0]) }, this.move=function(){ alert(2) } } var obj=new DragListener(); document.getElementById("hello").onmousedown =obj.down;//正确 但我们在方法中用this访问到的对象是 dom document.getElementById("hello").onmousemove = obj.move;//正确 document.getElementById("hello").onmousedown =associateObjWithEvent(obj,'down');//正确 document.getElementById("hello").onmousemove = associateObjWithEvent(obj,'move');//正确
复制代码

改进的例子,无限参数

复制代码
function associateObjWithEvent(obj, methodName){ slice = Array.prototype.slice; var args=slice.call(arguments,2);//从第三个参数开始赋值 return (function(e){ e = e||window.event; var array = slice.call(arguments, 0); array.push(e); //第一个参数为event return obj[methodName].apply(this,array.concat(args));//第二个参数,依次... }); } function DhtmlObject(elementId){ var el = document.getElementById(elementId); if(el){ //el.onclick = associateObjWithEvent(this, "doOnClick"); el.onmouseover = associateObjWithEvent(this, "doMouseOver","hello2","aaa",event);//第一个参数为event,hello2是第二个参数, //因为event只有在事件处理函数执行时才会知道是具体什么事件类型,无法通过传参提供,传参的event只能识别为null
} } DhtmlObject.prototype.doMouseOver = function(event){ // doMouseOver 方法体。 alert("this:"+this.id);//this:hello alert("arg0:"+arguments[0]) ;//arg0:MouseEvent alert("arg1:"+arguments[1]);//arg1:hello2 alert("arg2:"+arguments[2]);//arg2:aaa alert("arg3-event:"+arguments[3]);//arg3-event:null alert("event:"+event);//event:MouseEvent } //DhtmlObject("hello"); 这样写,反而出错。因为是类的写法,必须实例化才会执行。 var hello=new DhtmlObject("hello")

5.为节点循环绑定click事件,在事件函数中使用当次循环的值或节点,而不是最后一次循环的值或节点

复制代码
//假设有两个a链接,id分别为"hello"、"world" function test(obj){ return function(){ alert(obj.id); } } var nodes=document.getElementsByTagName("a"); for(var i=0;i<nodes.length;i++){ var node=nodes[i] nodes[i].attachEvent("onclick", function(){alert(node.id)})//点击链接时,hello链接弹出world,world链接也弹出world; //这是因为循环完毕后,node被赋值为world元素 //这不是我们预期的结果!!! //正确写法一
// 内部参数parm为任意指定的参数,如:(function(node){return function(){alert(node.id)}})(node) nodes[i].attachEvent("onclick",(function(parm){return function(){alert(parm.id)}})(node)) //第一次循环创建了一个闭包,缓存的node参数为hello链接。 //第二次循环又创建了一个闭包,缓存的node参数为world链接。 //点击链接时,hello链接弹出hello,world链接弹出world,因为他们调用的是各自的node参数 //正确写法二 var func=test(node); nodes[i].attachEvent("onclick",func) }
复制代码

6.暂停执行

出处参考:http://ljchow.cnblogs.com

这个可以做很多实用和有意思的交互。

这个是无忧上月MM的例子,用闭包实现程序的暂停执行功能,还蛮创意的。

复制代码
<input type="button" value="继续" onclick='st();'/> <script type="text/javascript"><!-- var st = (function() { alert(1); alert(2); return function() { alert(3); alert(4); } })(); // --></script>
复制代码

把这个作用延伸下,我想到了用他来实现window.confirm。

复制代码
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript"> var $ = function(id) { return "string" == typeof id ? document.getElementById(id) : id; } var doConfirm = function(divId) { $(divId).style.display = ""; function closeDiv() { $(divId).style.display = "none"; } return function(isOk) { if (isOk) { alert("Do deleting..."); } closeDiv(); }
} </script> <style type="text/css"> body { font-family: Arial; font-size: 13px; background-color: #FFFFFF; } #confirmDiv { width: 200px; height: 100px; border: dashed 1px black; position: absolute; left: 200px; top: 150px; } </style> </head> <body> <div> <input name="btn2" type="button" value="删除" onclick="doConfirm('confirmDiv');" /> <div id="confirmDiv" style="display: none;"> <div style='position: absolute; left: 50px; top: 15px;'> <p> 你确定要删除吗?</p> <input type="button" value="确定" onclick="doConfirm('confirmDiv')(true);" /> <input type="button" value="取消" onclick="doConfirm('confirmDiv')(false);" /> </div> </div> </div> </body> </html>
复制代码

7.包装相关功能

下面的代码定义了一个函数,这个函数用于返回一个 HTML 字符串,其中大部分内容都是常量,但这些常量字符序列中需要穿插一些可变的信息,而可变的信息由调用函数时传递的参数提供。

通过执行单行函数表达式返回一个内部函数,并将返回的函数赋给一个全局变量,因此这个函数也可以称为全局函数。而缓冲数组被定义为外部函数表达式的一个局部变量。它不会暴露在全局命名空间中,而且无论什么时候调用依赖它的函数都不需要重新创建这个数组。

如果一个函数依赖于另一(或多)个其他函数,而这些其他函数又没有必要被其他代码直接调用,那么可以运用相同的技术来包装这些函数,而通过一个公开暴露的函数来调用它们。这样,就将一个复杂的多函数处理过程封装成了一个具有移植性的代码单元。

复制代码
/* 声明一个全局变量 - getImgInPositionedDivHtml - 并将一次调用一个外部函数表达式返回的内部函数赋给它。 这个内部函数会返回一个用于表示绝对定位的 DIV 元素 包围着一个 IMG 元素 的 HTML 字符串,这样一来, 所有可变的属性值都由调用该函数时的参数提供: */ var getImgInPositionedDivHtml = (function(){ /* 外部函数表达式的局部变量 - buffAr - 保存着缓冲数组。 这个数组只会被创建一次,生成的数组实例对内部函数而言永远是可用的 因此,可供每次调用这个内部函数时使用。 其中的空字符串用作数据占位符,相应的数据 将由内部函数插入到这个数组中: */ var buffAr = [ '<div id="', '', //index 1, DIV ID 属性 '" style="position:absolute;top:', '', //index 3, DIV 顶部位置 'px;left:', '', //index 5, DIV 左端位置 'px;width:', '', //index 7, DIV 宽度 'px;height:', '', //index 9, DIV 高度 'px;overflow:hidden;\"><img src=\"', '', //index 11, IMG URL '\" width=\"', '', //index 13, IMG 宽度 '\" height=\"', '', //index 15, IMG 调蓄 '\" alt=\"', '', //index 17, IMG alt 文本内容 '\"><\/div>' ]; /* 返回作为对函数表达式求值后结果的内部函数对象。 这个内部函数就是每次调用执行的函数 - getImgInPositionedDivHtml( ... ) - */ return (function(url, id, width, height, top, left, altText){ /* 将不同的参数插入到缓冲数组相应的位置: */ buffAr[1] = id; buffAr[3] = top; buffAr[5] = left; buffAr[13] = (buffAr[7] = width); buffAr[15] = (buffAr[9] = height); buffAr[11] = url; buffAr[17] = altText; /* 返回通过使用空字符串(相当于将数组元素连接起来) 连接数组每个元素后形成的字符串: */ return buffAr.join(''); }); //:内部函数表达式结束。 })(); /*^^- :单行外部函数表达式。*/
//输出HTML字符串 document.write(getImgInPositionedDivHtml("www.baidu.com","hello",200,100,100,40,"hello"))
复制代码

相关文章推荐

闭包的应用场景一

[Don't use closures unless you really need closure  semantics.]不要使用闭包,除非你真正需要它。 [In most cases, n...

js闭包二:应用场景

原文地址:http://blog.csdn.net/yanghua_kobe/article/details/6780181 场景一:采用函数引用方式的setTimeout调用 ...

Android兼容包Support v4.v7.v13区别与应用场景

引言Google为了在较低版本中兼容高版本的控件和布局以及相关的一些主题(Theme),推出了兼容包,方便开发人员在较低版本中使用高版本的效果。因此,我们经常会看到v4,v7,v13,v14,v17等...

大数据在银行业的应用场景

随着银行业务的载体与社交媒体、电子商务的融合越来越紧密,仅对原有15%的结构化数据进行分析已经不能满足发展的需求。企业需要借助大数据战略打破数据 边界,囊括85%的大数据分析,来构建更为全面的企业运营...
  • fhx007
  • fhx007
  • 2013年11月05日 22:31
  • 4587

大数据应用场景及相关技术

  • 2015年04月06日 14:52
  • 24KB
  • 下载

机器学习的应用场景

  • 2017年03月29日 15:29
  • 3.64MB
  • 下载

回归算法应用场景实例二十则

本文整理了12个天池、DataCastle、CCF中可使用回归算法处理的问题场景。 1 机场客流量分布预测         为了有效利用机场资源,机场正利用大数据技术,提升生产运营的效率。机场内需要不...

UML设计的9种图例 区别 应用场景

  • 2014年01月04日 14:23
  • 259KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:闭包的应用场景
举报原因:
原因补充:

(最多只允许输入30个字)