javascript事件系統的發展史2

來源:互聯網
上載者:User

綜觀一個系統的發展,無非是發現一個問題就把它獨立出來解決掉,因此它的所有模組(或者分支)其實針對獨立的問題,這樣我們對這些問題若有什麼更好的解決方案,替換相應的模組便是,要不代碼混雜在一起沒法看了。上一部分就提到了,attachEvent的事件列隊出了些問題,我們要手動構建列隊了。所謂列隊就是有先有後的問題,因此DE大神搞了一個全域的uuid,分配給每個回呼函數。但要注意,每個回調所針對的事件來源與事件類型,因此這不可能一個數組搞定。看:

el.attachEvent("onmouseenter",function(){alert(window.event.type)}) |       |             |             |                |         |       |             |             |                |事件來源 監聽器        事件類型       回呼函數          事件對象

事件來源,其實el也一定是事件來源,如某同類型事件從文檔樹下冒泡上來,或者頂層對象要捕獲它下面的某個同類型事件,那它就成了currentTarget。為簡單起見,我稱它為事件來源。這個事件來源可以綁定很多監聽器,每個監聽器可以針對不同的事件類型,當然這也意味著有許多回呼函數。當我們發現attachEvent多個同類型事件時,回呼函數的次序出錯時,意味著,如果我們還要它時,每種事件類型只能綁定監聽器一次。那不就意味著,一個監聽器只有一個回呼函數,因為只有一個次序肯定不會出錯!但怎麼添加更多回呼函數呢?嗯,我們需要一個數組。數組裡面的回呼函數才是我們自己添加的,然後一個for迴圈執行它們就是!

      var queue = [];         el.attachEvent("onmouseenter",function(){        var e = window.event;        for(var i=0,n=queue.length;i<n;i++){          queue[i].call(this,e)//queue[i]為我們自己的回呼函數,this指向el        }      });      queue.push(callback)

這樣就實現監聽事件與添加回呼函數相分離。但是一個事件類型就要搞一個queue,而且這些事件類型即使是同一類型還有事件來源之分。那會產出巨量的全域變數。因此我們必須找個地方放置它們。DE大神在要監聽的每個元素節點(或者文檔對象什麼的)上設定一個events屬性,那是一個對象,然後以type-object的形式儲存,type即為事件類型,object為一個對象,鍵為他的那個uuid,值為回呼函數。下面是他的addEvent函數的一部分,完整代碼這裡(要翻牆):

function addEvent(element, type, handler) {    if (!handler.$$guid) handler.$$guid = addEvent.guid++;    if (!element.events) element.events = {};    var handlers = element.events[type];    if (!handlers) {      handlers = element.events[type] = {};      if (element["on" + type]) {        handlers[0] = element["on" + type];      }    }    handlers[handler.$$guid] = handler;    element["on" + type] = handleEvent;  };

下面是它的events的結構:

        element.events = {          "click":{            1:fn1,            3:fn3,            4:fn4          },          "mousemove":{            2:fn2,            5:fn5          }        }

這樣儲存結構註定了只能用最慢的for...in迴圈來遍曆。嘛,限於當時人們的眼界,大神已經做得很好了。最佳的結構應該能對應我上面給出的樣本,應該是這個樣子:

        element.events = {          "click":[fn1,fn3,fn4],          "mousemove":[fn2,fn5]        }

縱觀DE的事件大神的事件系統,可是當時是複雜的,是具模組化的,每個針對不同的問題進行處理:

addEvent //為事件來源某一個事件類型添加一個監聽器(它的回呼函數總是主處理器),並把回呼函數放置到事件來源的events屬性上removeEvent //為事件來源某一個事件類型逐一刪減回呼函數,handleEvent//主處理器,用於在裡面遍曆我們自己添加的回呼函數fixEvent//也是在主處理器中執行,並只針對IE的事件來源對象,為它添加標準瀏覽器的兩個方法

IE7發布後,引入新的記憶體流失。在IE7中,DOM對象不會被CG程式回收,只有離開頁面時會被回收,但如果這時還被東西引用著就完蛋了,所以我們要清空元素節點上面的東西。如果我們把這個毛病解決了就很完美了,無奈DE大神不幹了,這個問題留給其他繼續發展的架構搞定。其中之一就是jQuery,它的事件系統就是基本DE大神的。jQuery面臨的任務有如下幾個:

  • 支援更多的事件類型,如FF下的滾輪事件,DOMMouseScroll是不能通過onXXX添加到事件來源上的。
  • 分離事件來源那個events屬性,交由新式的緩衝系統集中管理。
  • 讓IE與標準瀏覽器的事件來源對象更加標準化。

至於事件代理,那是更後的事了。它最初都是由外掛程式引入的,然後逐步發展到今日的規模。至於上面三點,我在《javascript 跨瀏覽器的事件系統》系列給出的相應的思路了:用緩衝系統把element.events從元素上分離出來,取而代之是一個輕量的uuid,其對應的緩衝體儲存事件類型與回呼函數的映射;建立一個偽事件對象將原生事件對象包裹起來,在它的原型添加w3c的事件方法;在fix函數中修正其事件屬性,如左中右鍵,座標,事件來源等亂七八糟的東西,這樣就幾乎以假亂真了;在handle函數,我們監視每次回呼函數執行的結果,如果為undefined就讓它冒泡,如果為false就禁止冒泡與預設行為,如果isImmediatePropagationStopped的執行結果為true就斷開迴圈,換言之,禁止同類型的回呼函數的繼續執行。這個東西是從Flash學回來的,稱之為stopImmediatePropagation。當然,對於DOMNodeInserted、 DOMNodeRemoved、 DOMNodeRemovedFromDocument、 DOMNodeInsertedIntoDocument、 DOMAttrModifiedonclick這樣進階的事件,onXXX是無能為力了,因此必須請回attachEvent與addEventListener。DOMMouseScroll。

      jQuery.event = {        add:function(){},//為事件來源某一個事件類型添加一個監聽器(它的回呼函數總是主處理器),並把回呼函數放到緩衝體中        remove:function(){},//把回呼函數從緩衝體中刪除,如果某一類型為空白,則移除相應的監聽器(DE大神的不會)        fix:function(){},//將事件對象用偽事件對象封裝起來,因為事件對象的屬性都是唯讀,這樣才能添加與修正更多標準的屬性        handle:function(){},//根據事件類型從緩衝體中取出,遍曆執行,並根據回呼函數的結果執行阻止冒泡與預設行為等方法        trigger:function(){}//實現跨平台的充許傳參的事件指派      }      jQuery.Event = function(){}//為事件對象添加w3c的那幾個標準方法與少量屬性

如果類庫看得多的話,就會知道全世界都在設法類比mouseenter/leave,這是IE特有的事件,它們的優越性可見這篇文章《Goodbye mouseover, hello mouseenter》。由於標準瀏覽器不支援,我們必須用其他類似事件類比它們。不用說,最近它們的是mouseover,mouseout。對於這樣特殊的事件,還有domReady,jQuery引入了special系統,一個事件的子系統,繞了大圈讓原生事件回到類比事件的事件列隊中。當然這樣做有一個弊端,mouseente/leave在標準瀏覽器的事件類型總是錯誤的……看來,這special系統還不成熟呢!

jQuery還引進入命名空間與事件指派,這對以後事件代理非常有用。那麼第三部分繼續!

  //利用同一個命名空間綁定三個事件  //$('a').bind('keydown.key keypress.key keyup.key', function () { alert("nasamidesu"); });  //然後卸載時就輕鬆了  //$('a').unbind('.key');

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.