事件委派
Web應用都是由事件驅動運轉的。我喜歡事件處理,尤其喜歡自己定義事件。
它能使你的産品可擴充,而不用改動核心代碼。
有一個很大的問題(也可以說是功能強大的表現),是關于頁面上事件的移除問題。你可以對某個元素安裝一個事件監聽器,事件監聽器就開始運轉工作。
但頁面上沒有任何訓示說明這有個監聽器。因為這種不可表現的問題 (這尤其讓一些新手頭疼) ,以及像IE6這樣的”浏覽器“在太多的使用事件監聽時會出現各種的記憶體問題,你不得不承認盡量少使用事件程式設計是個明智的做法。
于是 事件委托 就出現了。
當頁面上某個元素上的事件觸發時,而在 DOM 繼承關系上,這個元素的所有子元素也能接收到這個事件,這時你可以使用一個在父元素上的事件處理器來處理,而不是使用一堆的各個子元素上的事件監聽器來處理。
究竟是什麼意思?這樣說吧,頁面上有很多超連結,你不想直接使用這些連結,想通過一個函數來調用這個連結,
HTML代碼是這樣的:
1 <h2>Great Web resources</h2>
2 <ul id="resources">
Curriculum</a></li>
9 </ul>
常見的做法是通過循環這些連結,将每個連結上附加一個事件處理器:
01 // 典型的事件處理例子
02 (function(){
03 var resources
= document.getElementById('resources');
04 var links =
resources.getElementsByTagName('a');
05 var all =
links.length;
06 for(var i=0;i
07 // Attach a listener to each link
08 links[i].addEventListener('click',handler,false);
09 };
10 function handler(e){
11 var x =
e.target; // Get the link that was
clicked
12 alert(x);
13 e.preventDefault();
14 };
15 })();
我們用一個事件處理器也能完成這項任務:
01 (function(){
02 var resources
03 resources.addEventListener('click',handler,false);
04 function handler(e){
05 var x =
e.target; // get the link tha
06 if(x.nodeName.toLowerCase()
=== 'a'){
07 alert('Event
delegation:' + x);
08 e.preventDefault();
09 }
10 };
11 })();
因為點選事件就發生在這些頁面元素裡,你要做的就是比較它們的 nodeName,找出應該回應這個事件的那個元素。
免責聲明:上面說的這兩個關于事件的例子,在所有浏覽器裡都能運作,除了IE6,在IE6上你需要使用一個事件模型,而不是簡單的W3C的标準實作。這也就是我們推薦使用一些工具包的原因。
這種方法的好處并不是僅限于把多個事件處理器縮減為一個。你想想,舉個例子,你需要動态的往這個連結表裡追加更多的連結。使用事件委托後,你就不需要做其它修改了;否則的話,你需要重新循環這個連結表,重新給每個連結安裝事件處理器。
匿名函數和子產品化
在JavaScript裡最令人懊惱的事情是變量沒有使用範圍。任何變量,函數,數組,對象,隻要不在函數内部,都被認為是全局的,這就是說,這個頁面上的其它腳本也可以通路它,而且可以覆寫重寫它。
解決辦法是,把你的變量放在一個匿名函數内部,定義完之後立即調用它。
例如,下面的寫法将會産生三個全局變量和兩個全局函數:
1 var name = 'Chris';
2 var age = '34';
3 var status = 'single';
4 function createMember(){
5 // [...]
6 }
7 function getMemberDetails(){
8 // [...]
9 }
如果這個頁面上的其它腳本裡也存在一個叫 status 的變量,麻煩就會出現。
如果我們把它們封裝在一個 myApplication 裡,
這個問題就迎刃而解了:
01 var myApplication
= function(){
02 var name = 'Chris';
03 var age = '34';
04 var status = 'single';
05 function createMember(){
06 // [...]
07 }
08 function getMemberDetails(){
09 // [...]
10 }
11 }();
但是,這樣一來,在函數外面就沒有什麼功能了。如果這是你需要的,那就可以了。你還可以省去函數的名稱:
01 (function(){
如果你想在函數外面也能使用裡面的東西,那就要做些修改。
為了能通路 createMember() 或 getMemberDetails(),
你需要把它們變成 myApplication的屬性,進而把它們暴露于外部的世界:
05 return{
06 createMember:function(){
07 // [...]
08 },
09 getMemberDetails:function(){
10 // [...]
11 }
12 }
13 }();
14 //
myApplication.createMember() 和
15 //
myApplication.getMemberDetails() 就可以使用了。
這被稱作 module 模式或 singleton。
Douglas Crockford 多次談到過這些,Yahoo User Interface Library YUI 裡對此有大量的使用。
但這樣一來讓我感到不便的是,我需要改變句式來使函數和變量能被外界通路。更甚者,調用時我還需要加上myApplication 這個字首。是以,我不喜歡這樣做,我更願意簡單的把需要能被外界通路的元素的指針導出來。這樣做後,反倒簡化了外界調用的寫法:
11 return{
12 create:createMember,
13 get:getMemberDetails
14 }
15 }();
16 //現在寫成 myApplication.get()
和 myApplication.create() 就行了。
我把這個稱作 “revealing
module pattern.”
可配置化
一旦我把所寫的JavaScript代碼釋出到這個世界上,就有人想改動它,通常是人們想讓它完成一些它本身完成不了的任務—但通常也是我寫的程式不夠靈活,沒有提供使用者可自定義的功能。
解決辦法是給你的腳本增加一個配置項對象。
我 曾經寫過一篇深入介紹JavaScript配置項對象的文章,下面是其中的要點:
在你的腳本了添加一個叫做 configuration 的對象。
這個對象裡面,存放所有人們在使用這個腳本時經常要改動的東西:
CSS ID 和類名稱;
按鈕的名稱,标簽字等;
諸如”每頁顯示圖檔數”的值, “圖像的顯示的尺寸”的值;
地點,位置,以及語言設定。
将這個對象作為一個公用屬性傳回給使用者,這樣使用者可以修改覆寫它。
通常情況下這是你程式設計過程中的最後一步要做的事情。我把這些集中表現在了一個例子裡: “Five
things to do to a script before handing it over to the next developer.”
實際上,你也希望你的代碼能夠讓人們很方面的使用,并且根據他們各自的需要進行一些改動。
如果你實作了這個功能,你會少收到一些抱怨你的腳本的人發送給你的讓你困惑的郵件,這些信件會告訴你,有人修改了你的腳本,而且很好用。
與背景互動
在這麼多年的程式設計經曆中,我所領悟到的一個重要的事情就是,JavaScript是一個很優秀的開發界面互動的語言,但如果用來處理數字或通路資料源,那就有點使不上勁了。
最初,我學習JavaScript,是用來替代Perl的,因為我很讨厭非要把代碼拷貝到 cgi-bin 檔案夾下才能使Perl運作。
後來,我明白了應該使用一種背景工作的語言來處理主要的資料,而不能什麼事情都讓JavaScript去做。更重要的是我們要考慮安全性和語言特征。
如果我通路一個Web service, 我可以擷取到JSON-P 格式的資料,在用戶端浏覽器裡我把它做各種各樣的資料轉換,
但當我有了伺服器時,我有了更多的方法來轉換資料,我可以在Server端生成JSON或HTML格式的資料傳回給用戶端,以及緩存資料等操作。
如果你事先了解了并準備了這些,你會長期收益,省去了很多頭疼的時間。
編寫适用各種浏覽器的程式是種浪費時間,使用工具包吧!
在我最初開始搞Web開發時,在通路頁面時,究竟是使用 document.all 還是使用 document.layers 的問題上痛苦的掙紮了很久。
我選擇了 document.layers,因為我喜歡任何層都是自己的document的思想 (而且我寫了太多的 document.write 來生成元素)。層模式最終失敗了,于是我開始使用 document.all。
當Netscape 6 公布隻支援 W3C DOM 模型時,我很高興,但其實使用者并不關心這些。使用者隻是看見這種浏覽器不能顯示大多數浏覽器都能正常顯示的東西—這是我們編碼的問題。我們編寫了短視的代碼,隻能運作在目前的環境下,而不幸的是,我們的運作環境卻在不停的改變。我已經浪費了太多的時間來處理對各種浏覽器各種版本相容的問題。善于處理這類問題提供了我一個好的工作機會。但現在我們不必在忍受這種痛苦了。一些工具包,例如 YUI, jQuery 以及Dojo 都能夠幫我們處理這類問題。
它們通過抽象各種接口實作來處理浏覽器的各種問題,像版本不相容,設計缺陷等,把我們從痛苦中解救出來。
除非你要測試某個Beta版的浏覽器,千萬不要在自己的程式裡添加修正浏覽器的缺陷的代碼,因為你很有可能當浏覽器已經修改了這個問題時,你卻忘了删除你的代碼。另一方面,完全依賴于工具包也是個短視的行為。工具包可以幫你快速的開發,但如果你不深入了解JavaScript,你也會做錯事。
本文轉自 wws5201985 51CTO部落格,原文連結:http://blog.51cto.com/wws5201985/751283,如需轉載請自行聯系原作者