天天看點

利用Delegator模式保護javascript程式的核心與提高執行性能 (轉)

在程式設計中,如果我們不想或不能夠直接操縱目标對象,我們可以利用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