以下有些觀點有些偏激.關於事件註冊的相關模式我相信會很快出現的,偶正在研究中. 1、關於javascript的apply和call函數 prototype.js中用了大量的apply和call函數,不注意會造成理解偏差。 官方解釋:應用某一對象的一個方法,用另一個對象替換當前對象。 apply與call的區別是第二個參數不同。apply是 數組或者arguments 對象。而call是逗號隔開的任何類型。 apply,call方法最讓人混淆的地方也是apply,call的特色。但最好不要濫用。 能改變調用函數的對象。如下例,函數中用到this關鍵字,這時候this代表的是apply,call函數的第一個參數。 <script src="prototype1.3.1.js"></script> <input type="text" id="myText" value="input text"> <script> function Obj(){ this.value="對象!"; } var value="global 變數"; function Fun1(){ alert(this.value); } window.Fun1(); Fun1.apply(window); Fun1.apply($('myText')); Fun1.apply(new Obj()); </script> 2、關於閉包 prototype.js在Class.create,bind等中用到javascript的閉包特色。但整體上prototype.js對於強大的閉包特性用的不多。大家可以參閱我翻譯的篇文章瞭解閉包 。 3、讓我比較反感的兩個方法 (1) var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } } 很討厭用別的語言的風格來寫javascript。用這個方法構造自訂類 並沒有覺得有多方便,減少程式碼數,只會讓人難理解,多定義一個initialize方法。 其實討厭這條有些牽強,不過修改Object的原型對象就有點過分了。 (2)Object.prototype.extend 先不過你取個extend的名字會讓熟悉java的人引起的歧義。修改Object的prototype就說不過去了。不知道作者是怎麼考慮的。當你for in迴圈對象是,麻煩就來了。可能有人會問你for in幹嗎。 我一個項目中既用了DWR,也用了prototype.js,dwr返回的javascript對象都多了個exetend屬性,還得特殊處理。 以前我比較過[URL=http://www.blogjava.net/zkjbeyond/archive/2006/05/08/45108.html]dojo和prototype.js中繼承的實現[URL],現在我明白個道理。對於javascript這種沒有靜態類型檢查,文法寬鬆的語言來講,如果你選擇了某個js類庫,那你也必須適應作者寫javascript的風格。prototype.js的作者對extend的使用爐火純青,如果我們不當它只是個屬性拷貝的函數的話,多讀讀prototype.js的代碼是好的。 4、關於函數的綁定 類庫提供了Function.prototype.bind Function.prototype.bindAsEventListener兩個方法。首先我們從概念上解釋一個這兩個方法。 任何一個函數都可以調用這兩個方法;參數的是javascript對象或網頁上元素對象;傳回型別是個函數對象。 本來我就是個函數,返回還是函數,到這兩個函數有什麼不同呢。看實現代碼,關鍵還是apply\call函數的代碼。其實這裡只是轉化了一下方法調用的對象。 <script src="prototype1.3.1.js"></script> <input type=checkbox id=myChk name="asf" value=1> Test <script> var CheckboxWatcher = Class.create(); CheckboxWatcher.prototype = { initialize: function(chkBox, message) { this.chkBox = $(chkBox); this.message = message; this.chkBox.onclick = this.showMessage.bindAsEventListener(this); }, showMessage: function(evt) { alert(this.message + ' (' + evt.type + ')'); } }; new CheckboxWatcher('myChk','message!!!!'); //$('myChk').onclick=function(){}; </script> 這是 https://compdoc2cn.dev.java.net/ 上舉的例子,個人感覺沒什麼意思,反而讓我對bind,bindAsEventListener有些反感。(javascript就是這樣,明明大家都知道的文法,但寫出來的代碼差別確很大) 看下面代碼: <script src="prototype1.3.1.js"></script> <input type=checkbox id=myChk name="chk" value=1> Test <script> function Class(){ this.name="class"; } Class.prototype.getName=function(){ alert(this.name); } var obj=new Class(); //$('myChk').onclick=obj.getName; $('myChk').onclick=obj.getName.bind(obj); //$('myChk').onclick=obj.getName.bind($('myChk')); </script> 從上面代碼可以看出bind/bindAsEventListener只是封裝了一下apply/call方法,改變方法的調用對象。如例子,你可以把obj.getName方法轉化成任何對象調用,並且把方法讓表單元素觸發。(bind和bindAsEventListener之間只是返回函數的參數不同) 這兩個方法也可以用在對象之間的方法重用,實作類別似繼承方法的概念。看以下代碼,其實是比較無聊的。 <script src="prototype1.3.1.js"></script> <script> function Class1(name){ this.name=name; } Class1.prototype.getName=function(){ alert(this.name); } function Class2(name){ this.name=name; this.getName=Class1.prototype.getName.bind(this); } var obj1=new Class2("yql"); obj1.getName(); var obj2=new Object(); obj2.name="zkj"; obj2.fun=Class1.prototype.getName.bind(obj2); obj2.fun(); </script> 我從來沒讀過prototype.js的擴充項目代碼,也不知道bind..的最佳實務,一起挖掘吧。但你絕對不要把bind/bindAsEventListener從綁定的詞義上來理解,可能會讓你更加迷惑。從apply/call理解本質。應用某一對象的一個方法,用另一個對象替換當前對象。 5、關於事件的註冊 <script src="prototype1.3.1.js"></script> <input type=checkbox id=myChk name="chk" value=1> Test <script> Event.observe(myChk, 'click', showMessage, false); //$('myChk').onclick=showMessage; //$('myChk').onclick=showMessage.bind(); $('myChk').onclick=showMessage.bind($('myChk')); function showMessage() { alert(this.value); } </script> 執行上面代碼,你就能明白Event.observe與bind/bindAsEventListener之間的區別: (1) 顯然Event.observe有限制,只能處理簡單的函數,並函數中不能有this之類的東西。 (2)Event.observe內部用到addEventListener/attachEvent。能把多個函數加到一個觸發事件(window.onload)。bind是覆蓋。 6、關於事件監聽最佳實務 很顯然prototype.js提供的事件註冊方法不是很完善。那看看dojo的時間註冊吧([URL=http://ajaxcn.org/space/Ajax/Dojo/Event]中文版[URL]),更加複雜,估計很多人像我一樣,對於dojo暫時持觀望態度。 如果你看過的前篇關於閉包的介紹,可能見過以下代碼。 看以下代碼前我想表述一個觀點,任何網頁中元素,瀏覽器都會為你建立一個對象([URL=http://www.blogjava.net/zkjbeyond/archive/2006/04/23/42666.html]見[URL])。(我覺得)這些對象與你建立javascript對象區別是它們有事件監聽,會響應滑鼠鍵盤的事件。如果你用了以下代碼,那麼把事件監聽代碼很好的轉化到你的javascript代碼中。 function associateObjWithEvent(obj, methodName){ return (function(e){ e = e||window.event; return obj[methodName](e, this); }); } function DhtmlObject(elementId){ var el = getElementWithId(elementId); if(el){ el.onclick = associateObjWithEvent(this, "doOnClick"); el.onmouseover = associateObjWithEvent(this, "doMouseOver"); el.onmouseout = associateObjWithEvent(this, "doMouseOut"); } } DhtmlObject.prototype.doOnClick = function(event, element){ ... // doOnClick method body. } DhtmlObject.prototype.doMouseOver = function(event, element){ ... // doMouseOver method body. } DhtmlObject.prototype.doMouseOut = function(event, element){ ... // doMouseOut method body. } 有時間我想用以上思想實現一個網頁浮動框拖拉的代碼(其實已經有很多了),待續........ 引用:ajaxcn.org 連結。謝謝dlee |