上一篇 js自定義消息機制研究學(xué)習(一) ——看百度搜索輸入提示 簡(jiǎn)單研究了一下百度首頁(yè)的代碼。
今天我來(lái)嘗試修改一下代碼,以使它更適合我們實(shí)際的研發(fā)情況。
首先,我們修改一下代碼,讓它可讀性稍微好一點(diǎn)。(原代碼參考上文)


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
}
}
})();
好了,現在我們有一個(gè)monitor對象了
現在來(lái)說(shuō)說(shuō)加入我們使用這個(gè)對象有可能要應對的情況。
一、將消息直接通知到函數
如果我們要簡(jiǎn)單監聽(tīng)某個(gè)對象里的某個(gè)消息,譬如下面代碼中這個(gè)對象里的sendData消息


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


obj1.bind("sendData",{handler:function(data){
alert(data.data);
}});
高興的事,我們很快寫(xiě)完了。我么可以不要{}呢,也不寫(xiě)handler么?
我們改改monitor,讓它能直接將消息發(fā)送到函數,對monitor的trigger內的方法做一個(gè)簡(jiǎn)單的更改,如下:


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);
}
}
}
這樣我們就可以直接將消息發(fā)送到函數。
當然,這也會(huì )給我們帶來(lái)一點(diǎn)小小的,因為加了if語(yǔ)句的性能損失。10000000次trigger的一個(gè)測試數據:1076(未修改前):1134(修改后)——單位毫秒
一個(gè)極端點(diǎn)的更改,我們只想把消息傳給函數,不傳給對象,那么修改如下:


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函數了,這樣的方式在一些簡(jiǎn)單的應用中效果也不錯,比如我們用jQuery的bind方法就可以實(shí)現很多我們要的效果,為了實(shí)現bind函數內this指向消息源頭,這里使用call方法就可,代碼:


這樣,我們可以基于一個(gè)或者數個(gè)復雜的對象做一些擴展開(kāi)發(fā),就像基于dom的click等事件來(lái)實(shí)現我們想要的效果一樣簡(jiǎn)單。
但如果涉及多個(gè)對象直接互相傳遞消息,只bind到函數就有點(diǎn)限制。如果不是特殊的需求,不建議用這種方式,最好bind到對象,兼容bind到對象和函數,也會(huì )讓我們少敲一些handler,因此也是個(gè)不錯的選擇
二、new的對象如何綁定monitor
當我們準備用js面向對象開(kāi)發(fā)時(shí),我們干:


function base()
{}
var obj=new base();
那么我們想要在new出來(lái)對象上使用monitor模式,少一點(diǎn)使用,我們可以monitor.ini(obj);
那么如果大量類(lèi)似對象要使用monitor模式呢?譬如


function Person(name)
{
this.name=name;
this.sayHello=function()
{
this.trigger({type:"say",msg:"hi,我是"+this.name})
}
}
我們要創(chuàng )建很多的對象,然后調用他們的sayHello,假設是很多人對不同的對象說(shuō)話(huà)的場(chǎng)景,我們創(chuàng )建一個(gè)Person對象就要monitor.ini一下,這種辦法很笨
假設你不想修改monitor的代碼,你可以這樣:
monitor.ini(Person.prototype);如果你確實(shí)想簡(jiǎn)單寫(xiě)成如下:
monitor.ini(Person);那么只好修改修改monitor代碼了:
首先是ini


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__ 這個(gè)的初始化,因為如果在prototype上綁定__MSG_QS__ 屬性的話(huà),每一個(gè)bind都會(huì )bind到所有對象上,這不是我們的本意,就例如我們希望的是每一個(gè)Person說(shuō)的話(huà),只能由聽(tīng)他說(shuō)話(huà)的人收聽(tīng)到。實(shí)現這樣的效果還需要在bind,trigger時(shí)做一些修改,如下:




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 }也去掉了
三、如何綁定類(lèi)消息
這里的類(lèi)消息是這樣的一種需求,比如接上例,我們要用一個(gè)logger記錄所有Person講的話(huà)(sayHello()),難道我們創(chuàng )建一百個(gè)Person,就要調用一百次bind么?假如只有一處代碼才能new Person()那還好說(shuō),不會(huì )增加我們多少的代碼量。但你的new Person已經(jīng)灑落到代碼各處,到處都是。OMG。怎么辦?
首先,再剽竊一個(gè)jQuery的命名:live,在monitor中加入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__ 來(lái)存儲類(lèi)級別的消息隊列
所以它是面向function對象的,把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


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__中獲?。╭ueue=this.__MSG_QS__會(huì )使兩者指向同一內存地址),因為這樣如果修改queue變量會(huì )直接修改掉__MSG_QS__隊列中的值,在這里用了兩次concat分別拷貝了對象消息監聽(tīng)者和類(lèi)消息接聽(tīng)者。
ok,現在我們可以一下就監聽(tīng)所有Person對象的消息了,代碼如下:
Person.live("say",{handler: function(data){
//logger
});
搞定,準備洗洗睡吧!
想要使用類(lèi)似monitor的人,根據自己的實(shí)際需求定制一下。比如你想在監聽(tīng)函數里調用消息源,可以把trigger中queue[a].handler(Y)修改為queue[a].handler(Y,this)等等
修改后的monitor


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. 可以直接用一個(gè)函數偵聽(tīng)消息
2. 可以將function注冊為monitor,然后所有function new出來(lái)的對象都將自動(dòng)綁定了monitor
3. 為function增加了live方法,可以方便偵聽(tīng)所有new對象的事件
4. 將消息源作為第二個(gè)參數傳給了方法。
附:
這只是簡(jiǎn)單的一些修改擴展,如果你沒(méi)有這些需要,簡(jiǎn)單原始的monitor就非常簡(jiǎn)潔高效,足夠應付一些簡(jiǎn)單的建模。
實(shí)際中我用的類(lèi)似的monitor模式,不是只有ini的一個(gè){ini:function()}對象,而是一個(gè)有bind,trigger,unbind,live,die等方法的function對象,使用繼承的方式(比如jQuery.extend)來(lái)為{}或new的對象綁定monitor的模式。因此增加了許多的條件判斷,性能上要比本文的monitor差一些。
這里的代碼都是寫(xiě)本文時(shí)隨手寫(xiě)的,難免有誤,歡迎指正。
聯(lián)系客服