在程式設計中,如果我們不想或不能夠直接操縱目标對象,我們可以利用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();
<!doctype html>
<html dir="ltr" lang="zh-CN">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<script type="text/javascript">
var ClassA = function(){
var _color = "red";
return {
getColor : function(){
document.write("<p>ClassA的執行個體的私有屬性_color目前是<span style='color:"+_color+"' >"+_color+"</span></p>");
},
setColor:function(color){
_color = color;
}
};
};
var delegate = function (client,clientMethod ){
return function() { return clientMethod.apply(client,arguments); }
}
window.onload = function(){
var a = new ClassA();
a.getColor();
a.setColor("green");
//alert(a._color);
document.write("<p>執行代理!</p>")
var d = delegate(a,a.setColor);
d("blue");
document.write("<p>執行完畢!</p>")
</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"
);
我們還可以搞一些很好玩的東西,下面的例子取自一個日本部落格。
Function.prototype.delegate = function(delegateFor){
return delegateFor.apply(null, arguments);
};
var Hoge = function(){
this.open = function (){
return 'this is Hoge#open';
};
this.close = function (){
return 'this is Hoge#close';
var Foo = function (){
this.name = 'foo';
return 'this is Foo#open';
return 'this is Foo#close';
var hoge = new Hoge;
var foo = new Foo;
alert(hoge.open()); // this is Hoge#open
alert(hoge.open.delegate(foo.open)); // this is Foo#open
alert(foo.open.delegate(hoge.open)); // this is Hoge#open
由于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
=
"javascript:void(0)"
>部落格園</
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
=
"javascript:void(0)"
>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.
}
<style type="text/css">
body{background:#fff;}
a { color:#8080C0;text-decoration:none;border-bottom:2px solid #fff;}
a:hover {color:#336699;border-bottom-color:#B45B3E;}
</style>
var nav = document.getElementById("nav");
var links = nav.getElementsByTagName("a");
for (var i=0,l = links.length; i <l; i++) {
links[i].onclick = function () {
alert(this.innerHTML);
return false;
}
}
<ul id="nav">
<li><a href="javascript:void(0)">部落格園</a></li>
<li><a href="http://www.blueidea.com/">藍色理想</a></li>
<li><a href="http://www.51js.com/html/bbs.html">無憂腳本</a></li>
<li><a href="http://www.javaeye.com/">javaeye</a></li>
<li><a href="javascript:void(0)">CSDN</a></li>
</ul>
新的方法,用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.
}
nav.onclick = function () {
var e = arguments[0] || window.event,
target = e.srcElement ? e.srcElement : e.target;
alert(target.innerHTML);
return false;
為什麼它會行得通呢?!因為DOM2.0的事件模型是這樣的,如果某個元素觸發一個事件,如onclick,頂層對象document就會發出一個事件流,随着DOM樹往目标元素流去,這就是傳說中的捕獲階段,也就是原Netscape的事件執行模式,沿途的元素如果綁定了事件,它是不會執行的!第二階段,就是到達了目标元素,執行它上面的綁定事件,但如果onclick隻是個空實作,當然是沒有效果啦!第三階級,就是起泡階級,原IE的事件執行模式,從目标元素往頂層元素折回,如果沿途有onclick事件,就随個觸發!是以我們是點選了a元素,但它的onclick事件為空,當事件流上浮到ul元素時,發現ul元素綁定了onclick事件,就執行當中的函數。如果ul的祖先元素也綁定了onclick事件呢?!繼續執行!有多少執行多少!看下面的例子:
var addEvent = (function () {
if (document.addEventListener) {
return function (el, type, fn) {
el.addEventListener(type, fn, false);
};
} else {
el.attachEvent('on' + type, function () {
return fn.call(el, window.event);
});
})();
var wrapper = document.getElementById("wrapper");
addEvent(wrapper,'click',function(){
alert("冒泡過程連我也驚動了!");
});
<div id="wrapper">
<ul id="nav">
<li><a href="javascript:void(0)">部落格園</a></li>
<li><a href="http://www.blueidea.com/">藍色理想</a></li>
<li><a href="http://www.51js.com/html/bbs.html">無憂腳本</a></li>
<li><a href="http://www.javaeye.com/">javaeye</a></li>
<li><a href="javascript:void(0)">CSDN</a></li>
</ul>
</div>
- Event delegation with jQuery
- Event delegation with Prototype
- Event delegation with YUI