Rumah > hujung hadapan web > tutorial js > js发布订阅者模式详细的个人理解

js发布订阅者模式详细的个人理解

大家讲道理
Lepaskan: 2017-04-11 10:39:40
asal
1768 orang telah melayarinya

先摘抄一下javascript设计模式与开发实践一书中的文字介绍//现实中的发布订阅模式   
小明最近看上了一套房子,到了售楼处之后才被告知,该楼盘的房子早已售罄。好在售楼MM告诉小明,不久后还有一些尾盘推出,开发商正在办理相关手续,手续办好后便可以购买。 但到底是什么时候,目前还没有人能够知道。于是小明记下了售楼处的电话,以后每天都会打电话过去询问是不是已经到了购买时间。除了小明,还有小红、小强、小龙也会每天向售楼处咨询这个问题。一个星期过后,售MM 决 定辞职,因为厌倦了每天回答1000 个相同内容的电话。

当然现实中没有这么笨的销售公司,实际上故事是这样的:小明离开之前,把电话号码留在了售楼处。售楼MM 答应他,新楼盘一推出就马上发信息通知小明。小红、小强和小龙也是一样,他们的电话号都被记在售楼处的花名册上,新楼盘推出的时候,售楼MM会翻开花名册,遍历上面的电话号码,依次发送一条短信来通知他们。在刚刚的例子中,发送短信通知就是一个典型的发布—订阅模式,小明、小红等购买者都是订阅者,他们订阅了房子开售的消息。售楼处作为发布者,会在合适的时候遍历花名册上的电话 号码,依次给购房者发布消息。    

可以发现,在这个例子中使用发布—订阅模式有着显而易见的优点。购房者不用再天天给售楼处打电话咨询开售时间,在合适的时间点,售楼处作为发布者会通知这些消息订阅者。购房者和售楼处之间不再强耦合在一起,当有新的购房者出现时,他只需把手机号码留在售楼处,售楼处不关心购房者的任何情况,不管购房者是男是女还是一只猴子。 而售 楼处的任何变动也不会影响购买者,比如售楼MM 离职,售楼处从一楼搬到二楼,这些 改变都跟购房者无关,只要售楼处记得发短信这件事情。    

首先要指定好谁充当发布者(比如售楼处);然后给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者(售楼处的花名册); 最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函 数(遍历花名册,挨个发短信)。另外,我们还可以往回调函数里填入一些参数,订阅者可以接收这些参数。这是很有必要的,比如售楼处可以在发给订阅者的短信里加上房子的单价、面积、容积率等信息,订阅者接收到这些信息之后可以进行各自的处理:

  • <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>观察者模式</title>
    </head>
    <body>
    <button id="count">点我</button>
    <p id="show"></p>
    <script>
        //我的个人理解
        //发布者首先需要一个对象来保存事件的回调函数,这个对象的属性就是事件类型,比如{"click":[fn1,fn2],"mouseover":[fn3,fn4]}
        //订阅事件:首先要判断这个事件类型是否存在当前的对象上,如果不存在那就初始化一下就是添加这个属性,并且给这个属性的值设置为空数组
        //如果已经存在了那么久开始把该对调函数push进去对应的属性的值, 该值为一个数组
        //发布事件:发布事件就要一个个的通知订阅者,还有就是你发布了什么事件。这里要有if判断,
        // 先从参数中取出要发布事件的key,然后获取到key对应的回调函数数组,取出数组后先判断数组是否存在或者数组长度是否为0,如果不存在或者为0
        //就结束程序,若上述条件不成立就执行for循环,一次遍历回调函数数组,并执行里面的函数,这里需要改变回调函数的this指向。防止丢之
        //this。
        //移出事件:先判断要移出的key的值是否为空,若空则退出程序否则再判断是否传入了要移出哪个回调函数,若空,则认为要移出全部的函数,就把数组长度设置为0
        //
      //下面是js设计模式与开发实践上的实现过程
        var Event=(function(){
                var clientList={},
                    listen,
                    trigger,
                    remove;
            listen=function(key,fn){
                if(!clientList[key]){
                    clientList[key]=[];
                }
                clientList[key].push(fn);
            };
            trigger=function(){
                var key=Array.prototype.shift.call(arguments),
                    cb=clientList[key];
                if(!cb||cb.length==0){
                    return false;//如果没有保存回调函数就退出程序
                }
                for(var i=0;i<cb.length;i++ ){
                    cb[i].apply(this,arguments);//为什么要用applly呢,这是为了把触发时传入的参数传入给cb[i]对应的回调函数里面使用并且还要改变this指向
                }
            };
            remove=function (key,fn) {
    
                var cb=clientList[key];
                if(!cb){
                    return false;//如果要移除的key的值为空就代表没有人订阅该key和设置回调函数则直接返回
                }
                if(!fn){//如果没有传入具体的回调函数,表示需要取消key对应的消息的所有订阅
                    cb&&(cb.length=0);//这里cb是已经存在了就把cb的length设置为0.我有疑问,到这里其实cb不会不存在了吧上面如果不存在就退出了如何会走到这一步?
                }
                else{
                    for(var l=0;l<cb.length;l++){
                        var _cb=cb[l];
                        if(_cb===fn){//寻找哪个和要删除的回调函数一样
                            cb.splice(l,1);//从l处删除一个元素
                        }
                    }
                }
            };
            return {
                listen:listen,
                trigger:trigger,
                remove:remove
            }//这里返回这个对象其实是相当于中介代理的意思,请看下面的实际例子解释:
    //我们给每个发布者对象都添加了listen 和trigger 方法,以及一个缓存列表clientList,
    //这其实是一种资源浪费。
    //小明跟售楼处对象还是存在一定的耦合性,小明至少要知道售楼处对象的名字是
    //才能顺利的订阅到事件。
    //因此返回一个通用的对象,不必知道这个对象的名字,只需要把这个对象的一个引用给一个变量就行了
        })();
        Event.listen("A",fn1=function(data){
            console.log(data);
        })
        Event.listen("A",fn2=function(data){
            console.log(data);
        })
        Event.remove("A",fn1);
        Event.trigger("A","猴赛雷啊");
    </script>
    <script>
        var a=(function(){
            var count=0;
            var btn=document.getElementById(&#39;count&#39;);
            btn.onclick=function () {
                Event.trigger(&#39;add&#39;,count++);
            }
        })();
        var b=(function () {
            var p=document.getElementById(&#39;show&#39;);
            Event.listen(&#39;add&#39;,function(data){
                p.innerHTML=data;
            })
        })();
    </script>
    </body>
    </html>
    Salin selepas log masuk
最后作者也交代了发布订阅模式的不利之处:
模块之间如果用了太多的全局发布—订阅模式来通信,那么模块与模块之间的联系就被隐藏到了背后。我们最终会搞不清楚消息来自哪个模块,或者消息会流向哪些模块,这又会给我们的维护带来一些麻烦,也许某个模块的作用就是暴露一些接口给其他模块调用。

Atas ialah kandungan terperinci js发布订阅者模式详细的个人理解. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan