1. 程式人生 > >js學習心得之js的自定義事件-基於觀察者模式的實現

js學習心得之js的自定義事件-基於觀察者模式的實現

GOF對觀察者模式的定義:Observer的意圖是定義物件之間的一種一(被觀察者)對多(觀察者)的關係,當一個物件的狀態發生改變時,所有依賴它的物件得到通知,並且會自動更新自己。

從這段經典的定義中,可以推測下,觀察者模式中的倆個物件各自應該擁有的特徵
1,被觀察者應該可以註冊觀察者,登出觀察者。
2,持有對其註冊的觀察者。
3,當自身改變時,依賴於特性一,其可以通知對其註冊的觀察者。
4,觀察者會自動更新自己。
5,被觀察者應該是獨立的,也就是說,觀察者影響其並不會影響被觀察者。

java與js的事件機制都可以說觀察者模式的應用場景。
在java的世界中藉助於
java.util.Observable
java.util.Observer

中這倆個類可以非常簡便的實現觀察者模式-(java的awt的事件機制在底層就是藉助觀察者模式實現的,這也可以算是搭建java世界中事件機制的支撐基礎類了)

對於js的事件,譬如,在一個DOM元素(被觀察者)上的單擊事件,觀察者(也就是在這個事件上的回撥函式)會關注這個很特別的時刻,並且做出反應。


在寫js的自定義事件之前,先看一下曾經令我十分好奇的EXT的自定義事件
部分程式碼來自 《深入淺出EXTJS》和《javascript高階程式設計》(第二版)
Person=function(name){
    this.name=name;
    this.addEvents('work','read');
};
Ext.extends(Person,Ext.util.Observable);

當自定義的類繼承了Ext.util.Observable後,自定義的類便可以支援事件。

var sz=new Person('sz');
sz.on('work',function(){//為Person的例項繫結監聽,即把觀察者註冊到要被觀察者
    Ext.Msg.alert('宋崢,在工作那!');
});

下面,來觸發這個事件,其實原理很簡單,就是讓'work'事件對應處理函式執行。
sz.fireEvent('work');//回撥函式執行

很神奇,其實這就是封裝的力量,EXT對原生js的封裝,讓實現自定義事件變的如此簡單。
來看看《javascript高階程式設計》(第二版)對是自定義事件的實現(有小改動)。
對EXT自定義的事件的實現的原理?是不是已經相當透徹了?是不是可以聯絡到觀察者設計模式?
  
    function Obserable(){
        this.handlers={};//持有事件處理函式
    }
    Obserable.prototype={//重寫EventTarget的原型物件
        constructor:EventTarget,
        on:function(type,handler){//對應於觀察者模式-在相應的被觀察者註冊觀察者
            //handlers中是否已有針對此事件型別的陣列
            //沒有建立一個空陣列
            //把這個處理函式推入對應的handlers[type]陣列,
            if(typeof this.handlers[type]=="undefined"){
                this.handlers[type]=[];
            }
            this.handlers[type].push(handler);
        },
        fireEvent:function(event){
            if(!event.target){
                event.target=this;
            }
            if(this.handlers[event.type] instanceof Array){
                var handlers=this.handlers[event.type];//檢出被觀察者註冊的觀察者
                for(var i=0;len=handlers.length;i++){
                    handlers[i]();//回撥函式執行,也就是觀察者更新自己
                }
            }
        }
    };
   
    function handlerMessage(event){
        alert("處理函式被回撥");
    }
    var ob=new Obserable();
    ob.on('message',handlerMessage);
    ob.fireEvent({type:'message'});

至此,只要繼承了此Obserable類的相應類的例項都可以成為被觀察者了,也就都可以支援事件了,這也就和EXT的自定義事件的思路沒多大差異了,當然EXT的設計更加巧妙。越來越有看EXT原始碼的慾望了,但水平還不夠,還需努力。

最後,不難發現,原生態的事件,是在某一個特殊時刻(單擊,獲得焦點等等)發生時,由遊覽器把這一特殊時刻的一些資訊,譬如事件型別,和其他的一些必要資訊封裝成event物件,觸發事件處理程式時把這個event物件作為實參傳入,而我們自定義的事件,當然只能由我們自己觸發,傳入的實參event也是我們自己傳入的(這個event,想封裝什麼就封裝什麼,需要什麼就封裝什麼).