您的位置:首页 > Web前端 > JavaScript

利用Delegator模式保护javascript程序的核心与提高执行性能 (转)

2009-08-10 08:47 483 查看
在编程中,如果我们不想或不能够直接操纵目标对象,我们可以利用delegate创建一个代理对象来调用目标对象的方法,从而达到操纵目标对象的目的。毋庸置疑,代理对象要拥有目标对象的引用。我们来看一下javascript的一个最简单实现:

1.
var
delegate=
function
(client,clientMethod){

2.
return
function
(){
return
clientMethod.apply(client,arguments);}

3.
}

4.
var
agentMethod=delegate(client,client.method)

5.
agentMethod();

<!doctypehtml>
<htmldir="ltr"lang="zh-CN">
<head>
<metacharset="utf-8"/>
<metahttp-equiv="X-UA-Compatible"content="IE=Edge">
<scripttype="text/javascript">
varClassA=function(){
var_color="red";
return{
getColor:function(){
document.write("<p>ClassA的实例的私有属性_color目前是<spanstyle='color:"+_color+"'>"+_color+"</span></p>");
},
setColor:function(color){
_color=color;
}
};
};
vardelegate=function(client,clientMethod){
returnfunction(){returnclientMethod.apply(client,arguments);}
}
window.onload=function(){
vara=newClassA();
a.getColor();
a.setColor("green");
a.getColor();
//alert(a._color);
document.write("<p>执行代理!</p>")
vard=delegate(a,a.setColor);
d("blue");
document.write("<p>执行完毕!</p>")
a.getColor();
};
</script>
<title>delegate</title>
</head>
<body>
</body>
</html>

运行代码

或者我们改变一下第二个参数,传个字符串进去:

1.
var
delegate=
function
(client,clientMethod){

2.
return
function
(){
return
client[clientMethod].apply(client,arguments);}

3.
}

4.
/****************略*******************/

5.
var
d=delegate(a,
"setColor"
);

我们还可以搞一些很好玩的东西,下面的例子取自一个日本博客。

<scripttype="text/javascript">
Function.prototype.delegate=function(delegateFor){
returndelegateFor.apply(null,arguments);
};

varHoge=function(){
this.open=function(){
return'thisisHoge#open';
};
this.close=function(){
return'thisisHoge#close';
};
};
varFoo=function(){
this.name='foo';
this.open=function(){
return'thisisFoo#open';
};
this.close=function(){
return'thisisFoo#close';
};
};

varhoge=newHoge;
varfoo=newFoo;

alert(hoge.open());//thisisHoge#open
alert(hoge.open.delegate(foo.open));//thisisFoo#open
alert(foo.open.delegate(hoge.open));//thisisHoge#open
</script>

运行代码

由于delegate实现目标对象的隐藏,这对于我们保护一些核心对象是非常有用的。不过,说实在javascript的delegate基本算是call与apply的傀儡,因为js中只有这2个方法提供了改变当前函数内部this作用域的功能。不过,要实现对类的委托而不是实例的委托,这就复杂得多。Prototype.js,YUI与JQuery都有相应的实现。具体自己可去参考它们的源码。接着下来,我将讲述delegate在事件上的应用,这可是个无以伦比的东东,就是改变侦听器的位置(改变事件的绑定对象)。或者说,它得益于javascript独特的事件传播机制,因此实现起来非常容易,大家要好好运行它,我以前也在富文本编辑器中用过。

01.
$.addEvent(colorPicker,
'click'
,
function
(){

02.
var
e=arguments[0]||window.event,

03.
td=e.srcElement?e.srcElement:e.target,

04.
nn=td.nodeName.toLowerCase();

05.
if
(nn==
'td'
){

06.
var
cmd=colorPicker.getAttribute(
"title"
);

07.
var
val=td.bgColor;

08.
_format(cmd,val);

09.
e.cancelBubble=
true
;

10.
colorPicker.style.display=
'none'
;

11.
}

12.
});

上面就是我在富文本编辑器撷取前景色与背景色的代码,注意,我是把事件绑定在颜色面板上,而不是面板上的那一个个格子上。为了直观起见,便于大家操作,我改用下面这个例子:

1.
<
ul
id
=
"nav"
>

2.
<
li
><
a
href
=
"http://www.cnblogs.com/"
>博客园</
a
></
li
>

3.
<
li
><
a
href
=
"http://www.blueidea.com/"
>蓝色理想</
a
></
li
>

4.
<
li
><
a
href
=
"http://www.51js.com/html/bbs.html"
>无忧脚本</
a
></
li
>

5.
<
li
><
a
href
=
"http://www.javaeye.com/"
>javaeye</
a
></
li
>

6.
<
li
><
a
href
=
"http://community.csdn.net/"
>CSDN</
a
></
li
>

7.
</
ul
>

现在我们要点击列表中链接,取出里面的内容,传统的方法,我们需要遍历添加所有a元素:

01.
window.onload=
function
(){

02.
var
nav=document.getElementById(
"nav"
);

03.
var
links=nav.getElementsByTagName(
"a"
);

04.
for
(
var
i=0,l=links.length;i<l;i++){

05.
links[i].onclick=
function
(){

06.
alert(
this
.innerHTML);

07.
return
false
;

08.
}

09.
}

10.
}

<!doctypehtml>
<htmldir="ltr"lang="zh-CN">
<head>
<metacharset="utf-8"/>
<metahttp-equiv="X-UA-Compatible"content="IE=Edge">
<styletype="text/css">
body{background:#fff;}
a{color:#8080C0;text-decoration:none;border-bottom:2pxsolid#fff;}
a:hover{color:#336699;border-bottom-color:#B45B3E;}
</style>
<scripttype="text/javascript">
window.onload=function(){
varnav=document.getElementById("nav");
varlinks=nav.getElementsByTagName("a");
for(vari=0,l=links.length;i<l;i++){
links[i].onclick=function(){
alert(this.innerHTML);
returnfalse;
}
}
}
</script>
<title>delegate</title>
</head>
<body>
<ulid="nav">
<li><ahref="http://www.cnblogs.com/">博客园</a></li>
<li><ahref="http://www.blueidea.com/">蓝色理想</a></li>
<li><ahref="http://www.51js.com/html/bbs.html">无忧脚本</a></li>
<li><ahref="http://www.javaeye.com/">javaeye</a></li>
<li><ahref="http://community.csdn.net/">CSDN</a></li>
</ul>
</body>
</html>

运行代码

新的方法,用nav代理了它下面所有元素,让它负责大家的onclick事件,包括它自己的,也不管是否为a元素,所以有时我们需要做一些判断,但少了遍历DOM树,效率明显提高。

01.
window.onload=
function
(){

02.
var
nav=document.getElementById(
"nav"
);

03.
nav.onclick=
function
(){

04.
var
e=arguments[0]||window.event,

05.
target=e.srcElement?e.srcElement:e.target;

06.
alert(target.innerHTML);

07.
return
false
;

08.
}

09.
}

<!doctypehtml>
<htmldir="ltr"lang="zh-CN">
<head>
<metacharset="utf-8"/>
<metahttp-equiv="X-UA-Compatible"content="IE=Edge">
<styletype="text/css">
body{background:#fff;}
a{color:#8080C0;text-decoration:none;border-bottom:2pxsolid#fff;}
a:hover{color:#336699;border-bottom-color:#B45B3E;}
</style>
<scripttype="text/javascript">
window.onload=function(){
varnav=document.getElementById("nav");
nav.onclick=function(){
vare=arguments[0]||window.event,
target=e.srcElement?e.srcElement:e.target;
alert(target.innerHTML);
returnfalse;
}
}
</script>
<title>delegate</title>
</head>
<body>
<ulid="nav">
<li><ahref="http://www.cnblogs.com/">博客园</a></li>
<li><ahref="http://www.blueidea.com/">蓝色理想</a></li>
<li><ahref="http://www.51js.com/html/bbs.html">无忧脚本</a></li>
<li><ahref="http://www.javaeye.com/">javaeye</a></li>
<li><ahref="http://community.csdn.net/">CSDN</a></li>
</ul>
</body>
</html>

运行代码

为什么它会行得通呢?!因为DOM2.0的事件模型是这样的,如果某个元素触发一个事件,如onclick,顶层对象document就会发出一个事件流,随着DOM树往目标元素流去,这就是传说中的捕获阶段,也就是原Netscape的事件执行模式,沿途的元素如果绑定了事件,它是不会执行的!第二阶段,就是到达了目标元素,执行它上面的绑定事件,但如果onclick只是个空实现,当然是没有效果啦!第三阶级,就是起泡阶级,原IE的事件执行模式,从目标元素往顶层元素折回,如果沿途有onclick事件,就随个触发!因此我们是点击了a元素,但它的onclick事件为空,当事件流上浮到ul元素时,发现ul元素绑定了onclick事件,就执行当中的函数。如果ul的祖先元素也绑定了onclick事件呢?!继续执行!有多少执行多少!看下面的例子:

<!doctypehtml>
<htmldir="ltr"lang="zh-CN">
<head>
<metacharset="utf-8"/>
<metahttp-equiv="X-UA-Compatible"content="IE=Edge">
<styletype="text/css">
body{background:#fff;}
a{color:#8080C0;text-decoration:none;border-bottom:2pxsolid#fff;}
a:hover{color:#336699;border-bottom-color:#B45B3E;}
</style>
<scripttype="text/javascript">
varaddEvent=(function(){
if(document.addEventListener){
returnfunction(el,type,fn){
el.addEventListener(type,fn,false);
};
}else{
returnfunction(el,type,fn){
el.attachEvent('on'+type,function(){
returnfn.call(el,window.event);
});
}
}
})();

window.onload=function(){
varnav=document.getElementById("nav");
nav.onclick=function(){
vare=arguments[0]||window.event,
target=e.srcElement?e.srcElement:e.target;
alert(target.innerHTML);
returnfalse;
}
varwrapper=document.getElementById("wrapper");
addEvent(wrapper,'click',function(){
alert("冒泡过程连我也惊动了!");
});
}
</script>
<title>delegate</title>
</head>
<body>
<divid="wrapper">
<ulid="nav">
<li><ahref="http://www.cnblogs.com/">博客园</a></li>
<li><ahref="http://www.blueidea.com/">蓝色理想</a></li>
<li><ahref="http://www.51js.com/html/bbs.html">无忧脚本</a></li>
<li><ahref="http://www.javaeye.com/">javaeye</a></li>
<li><ahref="http://community.csdn.net/">CSDN</a></li>
</ul>
</div>
</body>
</html>

运行代码

正由于这个特性,我们就可以利用ul的onclick去代理它下面所有元素的onclick事件。原来,我们需要给这些a元素准备五个侦听器(EventListener),现在我们只需要1个,节省了4个,如果这个列表有一百行呢?就节省了99个!在商务应用,我们经过会遇到许多报表(grid,实质是用table做的),我们需要为每行添加悬浮变色效果与点击编辑功能,这就用到onmouseover、onmouseout与onclick事件,如果这个报表有五千行,我们也只需要三个侦听器,节省了14997个!如果用传统方法做这个grid,IE6这样垃圾的游览器不卡到你吐血。因此,善用eventdelegation会大大提高我们程序的性能。好了,文章到此为止,再见。

附上一些有用的链接:

EventdelegationwithjQuery
EventdelegationwithPrototype
EventdelegationwithYUI
http://blog.xole.net/article.php?id=632http://htmlblog.net/bubble-menu-javascript-or-playing-with-yuis-event-delegation/
来源:/article/6948410.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐