程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> JSP編程 >> 關於JSP >> js自定義消息機制研究學習(二)——做一些改動,定制自己的消息機制

js自定義消息機制研究學習(二)——做一些改動,定制自己的消息機制

編輯:關於JSP

今天我來嘗試修改一下代碼,以使它更適合我們實際的研發情況。

 

首先,我們修改一下代碼,讓它可讀性稍微好一點。(原代碼參考上文)

 

monitor.js

var  monitor= (function(){        function bind(b){            var queue = this.__MSG_QS__;            if (!queue[b]) {                queue[b] = []            }            for (var a = 1, X = arguments.length, Y; a < X; a++) {                queue[b].push(arguments[a])            }        }        function trigger(Y){            var queue = this.__MSG_QS__[Y.type];            if (queue == null) {                return            }            for (var a = 0, X = queue.length; a < X; a++) {                queue[a].handler(Y)            }        }        return {            ini: function(X){                X.__MSG_QS__ = {};                X.bind = bind;                X.trigger = trigger;                return X            }        }})();好了,現在我們有一個monitor對象了

 

 

現在來說說加入我們使用這個對象有可能要應對的情況。

 

 

 

一、將消息直接通知到函數

 

如果我們要簡單監聽某個對象裡的某個消息,譬如下面代碼中這個對象裡的sendData消息

 

View Code

var obj1=monitor.ini({sendData:function(){    this.trigger({type:"sendData",data:"1"});}});我們只是想簡單的將這個要發送的數據alert一下,代碼如下:

 

View Code

obj1.bind("sendData",{handler:function(data){   alert(data.data);}});  

 

高興的事,我們很快寫完了。我麼可以不要{}呢,也不寫handler麼?

 

我們改改monitor,讓它能直接將消息發送到函數,對monitor的trigger內的方法做一個簡單的更改,如下:

 

View Code

function trigger(Y){            var queue = this.__MSG_QS__[Y.type];            if (queue == null) {                return            }            for (var a = 0, X = queue.length; a < X; a++) {               if(queue[a].handler)               {                   queue[a].handler(Y)               }               else               {                   queue[a](Y);                }            }        }  

 

 

這樣我們就可以直接將消息發送到函數。

 

當然,這也會給我們帶來一點小小的,因為加了if語句的性能損失。10000000次trigger的一個測試數據:1076(未修改前):1134(修改後)——單位毫秒

 

 

一個極端點的更改,我們只想把消息傳給函數,不傳給對象,那麼修改如下:

 

 

monitor 只傳函數

var  monitor= (function(){        function bind(b){            var queue = this.__MSG_QS__;            if (!queue[b]) {                queue[b] = []            }            for (var a = 1, X = arguments.length, Y; a < X; a++) {               queue[b].push(arguments[a])            }        }        function trigger(Y){            var queue = this.__MSG_QS__[Y.type];            if (queue == null) {                return            }            for (var a = 0, X = queue.length; a < X; a++) {                   queue[a](Y);             }        }        return {            ini: function(X){                X.__MSG_QS__ = {};                X.bind = bind;                X.trigger = trigger;                return X            }        }})();

這樣,我們只能bind函數了,這樣的方式在一些簡單的應用中效果也不錯,比如我們用jQuery的bind方法就可以實現很多我們要的效果,為了實現bind函數內this指向消息源頭,這裡使用call方法就可,代碼:

 

View Code

這樣,我們可以基於一個或者數個復雜的對象做一些擴展開發,就像基於dom的click等事件來實現我們想要的效果一樣簡單。

 

但如果涉及多個對象直接互相傳遞消息,只bind到函數就有點限制。如果不是特殊的需求,不建議用這種方式,最好bind到對象,兼容bind到對象和函數,也會讓我們少敲一些handler,因此也是個不錯的選擇

 

 

 

 

 

 

二、new的對象如何綁定monitor

 

 

當我們准備用js面向對象開發時,我們干:

 

View Code

function base(){}var obj=new base();那麼我們想要在new出來對象上使用monitor模式,少一點使用,我們可以monitor.ini(obj);

 

那麼如果大量類似對象要使用monitor模式呢?譬如

 

View Code

function Person(name){    this.name=name;    this.sayHello=function()    {        this.trigger({type:"say",msg:"hi,我是"+this.name})    }}  我們要創建很多的對象,然後調用他們的sayHello,假設是很多人對不同的對象說話的場景,我們創建一個Person對象就要monitor.ini一下,這種辦法很笨

 

假設你不想修改monitor的代碼,你可以這樣:

 

monitor.ini(Person.prototype);  如果你確實想簡單寫成如下:

 

monitor.ini(Person);  那麼只好修改修改monitor代碼了:

 

首先是ini

 

View Code

ini: function(X){                if(Object.prototype.toString.call(X)=="[object Function]")                {                    var proto=X.prototype;                    proto.bind = bind;                    proto.trigger = trigger;                                    }                X.bind = bind;                X.trigger = trigger;                return X            } 我去掉了__MSG_QS__ 這個的初始化,因為如果在prototype上綁定__MSG_QS__ 屬性的話,每一個bind都會bind到所有對象上,這不是我們的本意,就例如我們希望的是每一個Person說的話,只能由聽他說話的人收聽到。實現這樣的效果還需要在bind,trigger時做一些修改,如下:

 

bind

 

trigger

function trigger(Y){            var qs=this.__MSG_QS__ || {};            var queue= qs[Y.type] || [];             for (var a = 0, X = queue.length; a < X; a++) {                   if(queue[a].handler)                   {                        queue[a].handler(Y)                   }                   else                   {                        queue[a].call(this,Y);;                    }            }        }  

 

 

 

 ***PS:我把if (queue == null) {return }也去掉了

 

 

 

 

三、如何綁定類消息

 

這裡的類消息是這樣的一種需求,比如接上例,我們要用一個logger記錄所有Person講的話(sayHello()),難道我們創建一百個Person,就要調用一百次bind麼?假如只有一處代碼才能new Person()那還好說,不會增加我們多少的代碼量。但你的new Person已經灑落到代碼各處,到處都是。OMG。怎麼辦?

 

首先,再剽竊一個jQuery的命名:live,在monitor中加入live代碼如下:

 

live

function live(b)        {            var queue = this.prototype.__STATIC_MSG_QS__;            if (!queue[b]) {                queue[b] = []            }            for (var a = 1, X = arguments.length, Y; a < X; a++) {                queue[b].push(arguments[a])            }        }這段代碼與bind區別不大,唯一的區別是它這裡使用了this.prototype.__STATIC_MSG_QS__ 而不是this.__MSG_QS__ 我們用__STATIC_MSG_QS__ 來存儲類級別的消息隊列

 

所以它是面向function對象的,把ini修改如下

 

ini

return {            ini: function(X){                if(Object.prototype.toString.call(X)=="[object Function]")                {                    var proto=X.prototype;                    proto.__STATIC_MSG_QS__={};                    proto.bind = bind;                    proto.trigger = trigger;                    X.live=live;                                     }                X.bind = bind;                X.trigger = trigger;                return X            }        }  如果ini的是function,我們就再function上面綁定了live方法,並且在prototype上增加了__STATIC_MSG_QS__

 

我們還需要修改一下trigger

 

trigger

function trigger(Y){            var queue =[];            var qs=this.__MSG_QS__ || {};             var sqs=this.__STATIC_MSG_QS__|| {};             queue= queue.concat(qs[Y.type] || []);            queue= queue.concat(sqs[Y.type] || []);            for (var a = 0, X = queue.length; a < X; a++) {               if(queue[a].handler)               {                   queue[a].handler(Y)               }               else               {                   queue[a].call(this,Y);                }            }        }  增加了一些trigger的負擔,queue不再直接指向this.__MSG_QS__中獲取(queue=this.__MSG_QS__會使兩者指向同一內存地址),因為這樣如果修改queue變量會直接修改掉__MSG_QS__隊列中的值,在這裡用了兩次concat分別拷貝了對象消息監聽者和類消息接聽者。

 

ok,現在我們可以一下就監聽所有Person對象的消息了,代碼如下:

 

Person.live("say",{handler: function(data){ //logger});  搞定,准備洗洗睡吧!

 

 

想要使用類似monitor的人,根據自己的實際需求定制一下。比如你想在監聽函數裡調用消息源,可以把trigger中queue[a].handler(Y)修改為queue[a].handler(Y,this)等等

 

修改後的monitor

 

View Code

var  monitor= (function(){        function bind(b){            var queue = this.__MSG_QS__=this.__MSG_QS__ || {};            if (!queue[b]) {                queue[b] = []            }            for (var a = 1, X = arguments.length, Y; a < X; a++) {                queue[b].push(arguments[a])            }        }                function live(b)        {            var queue = this.prototype.__STATIC_MSG_QS__;            if (!queue[b]) {                queue[b] = []            }            for (var a = 1, X = arguments.length, Y; a < X; a++) {                queue[b].push(arguments[a])            }        }                function trigger(Y){            var queue =[];            var qs=this.__MSG_QS__ || {};             var sqs=this.__STATIC_MSG_QS__|| {};           queue= queue.concat(qs[Y.type] || []);            queue= queue.concat(sqs[Y.type] || []);            for (var a = 0, X = queue.length; a < X; a++) {               if(queue[a].handler)               {                   queue[a].handler(Y,this)               }               else               {                   queue[a].call(this,Y,this);                }            }        }        return {            ini: function(X){                if(Object.prototype.toString.call(X)=="[object Function]")                {                    var proto=X.prototype;                    proto.__STATIC_MSG_QS__={};                    proto.bind = bind;                    proto.trigger = trigger;                    X.live=live;                                     }                X.bind = bind;                X.trigger = trigger;                return X            }        }})();它多了如下一些特性:

 

1. 可以直接用一個函數偵聽消息

 

2. 可以將function注冊為monitor,然後所有function new出來的對象都將自動綁定了monitor

 

3. 為function增加了live方法,可以方便偵聽所有new對象的事件 

 

4. 將消息源作為第二個參數傳給了方法。

 

 

附:

 

這只是簡單的一些修改擴展,如果你沒有這些需要,簡單原始的monitor就非常簡潔高效,足夠應付一些簡單的建模。

 

 

實際中我用的類似的monitor模式,不是只有ini的一個{ini:function()}對象,而是一個有bind,trigger,unbind,live,die等方法的function對象,使用繼承的方式(比如jQuery.extend)來為{}或new的對象綁定monitor的模式。因此增加了許多的條件判斷,性能上要比本文的monitor差一些。

 

這裡的代碼都是寫本文時隨手寫的,難免有誤,歡迎指正。

 

預告:

 

下一篇,我會分享一下基於最後這個版本monitor的插件模式的嘗試,熱烈歡迎你的關注。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved