JavaScript中发布/订阅模式(观察者模式)

2019-11-02 admin

发布/订阅模式的前身-观察者模式

观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新。观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯。

观察者模式有一个别名叫“发布-订阅模式”,或者说是“订阅-发布模式”,订阅者和订阅目标是联系在一起的,当订阅目标发生改变时,逐个通知订阅者。

什么是发布/订阅模式

其实24种基本的设计模式中并没有发布订阅模式,上面也说了,他只是观察者模式的一个别称。 但是经过时间的沉淀,似乎他已经强大了起来,已经独立于观察者模式,成为另外一种不同的设计模式。 在现在的发布订阅模式中,称为发布者的消息发送者不会将消息直接发送给订阅者,这意味着发布者和订阅者不知道彼此的存在。在发布者和订阅者之间存在第三个组件,称为消息代理或调度中心或中间件,它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。

根本作用

  • 广泛应用于异步编程中(替代了传递回调函数)
  • 对象之间松散耦合的编写代码

基本案例介绍

背景:成都老妈兔头真香,买的人太多需要预定才能买到,所以顾客就等于了订阅者,订阅老妈兔头。 而老妈兔头有货了得通知顾客来买啊,不然没有钱赚,得通知所有的订阅者有货了来提兔头,这时老妈兔头这家店就是发布者。

/*兔头店*/        
var shop={
    listenList:[],//缓存列表
    addlisten:function(fn){//增加订阅者
        this.listenList.push(fn);
    },
    trigger:function(){//发布消息
        for(var i=0,fn;fn=this.listenList[i++];){
            fn.apply(this,arguments);
        }
    }
}

/*小明订阅了商店*/
shop.addlisten(function(taste){
    console.log("通知小明,"+taste+"味道的好了");
});
/*小龙订阅了商店*/
shop.addlisten(function(taste){
    console.log("通知小龙,"+taste+"味道的好了");
});
/*小红订阅了商店*/
shop.addlisten(function(taste){
    console.log("通知小红,"+taste+"味道的好了");
});    

// 发布订阅
shop.trigger("中辣");

//console
通知小明,中辣味道的好了
通知小龙,中辣味道的好了
通知小红,中辣味道的好了

案例升级

上面的案例存在问题,因为在触发的时候是将所以的订阅都触发了,并没有区分和判断,所以需要一个Key来区分订阅的类型,并且根据不同的情况触发。而且订阅是可以取消的。

升级思路:

  • 创建一个对象(缓存列表)
  • addlisten方法用来把订阅回调函数fn都加到缓存列表listenList中
  • trigger方法取到arguments里第一个当做key,根据key值去执行对应缓存列表中的函数
  • remove方法可以根据key值取消订阅
/*兔头店*/        
var shop={
    listenList:{},//缓存对象
    addlisten:function(key,fn){

        // 没有没有key给个初值避免调用报错
        if (!this.listenList[key]) {
            this.listenList[key] = [];
        }
        // 增加订阅者,一个key就是一种订阅类型
        this.listenList[key].push(fn);
    },
    trigger:function(){
        const key = Array.from(arguments).shift()
        const fns = this.listenList[key]

        // 这里排除两种特殊情况,第一种为触发的一种从未订阅的类型,第二种订阅后取消了所有订阅的
        if(!fns || fns.length===0){
            return false;
        }

        // 发布消息,触发同类型的所有订阅,
        fns.forEach((fn)=>{
            fn.apply(this,arguments);
        })

/*         for(var i=0,fn;fn=fns[i++];){
            fn.apply(this,arguments);
        } */

    },

    remove:function(key,fn){
        var fns=this.listenList[key];//取出该类型的对应的消息集合
        if(!fns){//如果对应的key没有订阅直接返回
            return false;
        }
        if(!fn){//如果没有传入具体的回掉,则表示需要取消所有订阅
            fns && (fns.length=0);
        }else{
            for(var l=fns.length-1;l>=0;l--){//遍历回掉函数列表
                if(fn===fns[l]){ // 这里是传入地址的比较,所以不能直接用匿名函数了
                    fns.splice(l,1);//删除订阅者的回掉
                }
            }
        }
    }

}

function xiaoming(taste){
    console.log("通知小明,"+taste+"味道的好了");
}

function xiaolong(taste){
    console.log("通知小龙,"+taste+"味道的好了");
}

function xiaohong(taste){
    console.log("通知小红,"+taste+"味道的好了");
}

// 小明订阅了商店
shop.addlisten('中辣',xiaoming);

shop.addlisten('特辣',xiaoming);

// 小龙订阅了商店
shop.addlisten('微辣',xiaolong);

// 小红订阅了商店
shop.addlisten('中辣',xiaohong);

// 小红突然不想吃了
shop.remove("中辣",xiaohong);

// 中辣口味做好后,发布订阅
shop.trigger("中辣");

shop.trigger("微辣");

shop.trigger("特辣");

发布-订阅的顺序探讨

我们通常所看到的都是先订阅再发布,但是必须要遵守这种顺序吗?答案是不一定的。如果发布者先发布一条消息,但是此时还没有订阅者订阅此消息,我们可以不让此消息消失于宇宙之中。就如同QQ离线消息一样,离线的消息被保存在服务器中,接收人下次登录之后,才会收到此消息。同样的,我们可以建立一个存放离线事件的堆栈,当事件发布的时候,如果此时还没有订阅者订阅这个事件,我们暂时把发布事件的动作包裹在一个函数里,这些包装函数会被存入堆栈中,等到有对象来订阅事件的时候,我们将遍历堆栈并依次执行这些包装函数,即重发里面的事件,不过离线事件的生命周期只有一次,就像qq未读消息只会提示你一次一样。

小结

发布-订阅的优势很明显,做到了时间上的解耦和对象之间的解耦,从架构上看,MVC,MVVM都少不了发布-订阅的参与。 同样的node中的EventEmitter也是发布订阅的

[转载]原文链接:https://segmentfault.com/a/1190000020886430

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-78778.html

文章标题:JavaScript中发布/订阅模式(观察者模式)

相关文章
Android中Okhttp3实现上传多张图片同时传递参数
之前上传图片都是直接将图片转化为io流传给服务器,没有用框架传图片。 最近做项目,打算换个方法上传图片。 Android发展到现在,Okhttp显得越来越重要,所以,这次我选择用Okhttp上传图片。 Okhttp目前已经更新到Okhttp...
2017-03-17
JavaScript实现PC手机端和嵌入式滑动拼图验证码三种效果
PC和手机端网站滑动拼图验证码效果源码,同时包涵了弹出式Demo,使用ajax形式提交二次验证码所需的验证结果值,嵌入式Demo,使用表单形式提交二次验证所需的验证结果值,移动端手动实现弹出式Demo三种效果 首先要确认前端使用页面,比如...
2017-03-17
jsdom 中文文档(纯翻译)
jsdom是一个纯粹由 javascript 实现的一系列 web标准,特别是 WHATWG 组织制定的DOM和 HTML 标准,用于在 nodejs 中使用。大体上来说,该项目的目标是模拟足够的Web浏览器子集,以便用于测试和挖掘真实世界...
2018-05-14
JavaScript常用特效chm下载
下载地址:JavaScript常用特效chm下载 对了,如果打开空白,在手册上右键属性解除锁定即可。 ...
2015-11-12
JS中的语音合成——Speech Synthesis API
JS中的语音合成——Speech Synthesis API 简介 HTML5中和Web Speech相关的API实际上有两类,一类是“语音识别(Speech Recognition)”,另外一个就是“语音合成(Speech Synthes...
2018-05-17
JavaScript教程:JS中的原型
Keith Peters 几年前发表的一篇博文,关于学习没有“new”的世界,其中解释了使用原型继承代替构造函数。两者都是纯粹的原型编码。 标准方法(The Standard Way) 一直以来,我们学习的在 JavaScript 里创建对...
2015-11-12
javascript是什么意思
avaScript是Netscape开发的一个对象脚本语言,它使用在世界各地数以百万计的网页和服务器应用程序上。 网景的JavaScript是ecma - 262版的标准脚本语言,和公布的标准只有轻微的差异。 与广为流行的错误理解相反,Ja...
2015-11-12
21天学通javascript
简介: 本书是Javascript入门教程。Javascript是Web开发中应用最早、发展最成熟、用户最多的脚本语言。其语法简洁,代码可读性在众多脚本语言中最好,它在使用时不用考虑数据类型,是真正意义上的动态语言。本书总分为四篇,共21章...
2015-11-16
React Native v0.13.3 发布,Facebook开源框架
React is a JavaScript library for building user interfaces. Just the UI: Lots of people use React as the V in MVC. Since...
2015-11-12
JavaScript的组成
一个完整的JavaScript由3个部分组成:核心(ECMAScript) 文档对象模型(DOM) 浏览器对象模型(BOM) ECMAScript 描述了该语言的语法和基本对象 ; DOM 描述了处理网页内容的方法和接口 ; BOM 描...
2015-11-12
回到顶部