欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

js自定义消息机制研究学习(二)——做一些改动,定制自己的消息机制

程序员文章站 2022-03-26 11:05:17
今天我来尝试修改一下代码,以使它更适合我们实际的研发情况。   首先,我们修改一下代码,让它可读性稍微好一点。(原代码参考上文)   monitor.js var&n...

今天我来尝试修改一下代码,以使它更适合我们实际的研发情况。

 

首先,我们修改一下代码,让它可读性稍微好一点。(原代码参考上文)

 

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的插件模式的尝试,热烈欢迎你的关注。