首页 > 代码库 > 转载:闭包的应用场景

转载:闭包的应用场景

什么是闭包?

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")

转载:闭包的应用场景