博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
javascript设计模式之发布订阅模式
阅读量:5095 次
发布时间:2019-06-13

本文共 4460 字,大约阅读时间需要 14 分钟。

  发布-订阅设计模式对大家来说并不是很陌生,举一个最简单的例子,在前端开发过程中,事件的绑定就是其实际的应用。首先我们先了解下什么是发布-订阅模式。

    基本概念:发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知。在前端开发中,我们一般用事件模型来替代传统的发布-订阅模式。

  发布-订阅模式是前端常用的一种设计模式,现在主流的MVVM框架,都大量使用了此设计模式,其主要作用有以下两点:一是可以实现模块间通信,而是可以在一定程度上实现异步编程。其基本事件模型如下:

  前端的事件绑定有三要素,一是传入事件类型,二是声明对应的回调方法,三是触发条件;触发条件为对应的事件类型。前端DOM的事件系统本质也是发布-订阅模式,而我们在业务处理中所应有的模式也与此类似,只不过发布订阅模式应用的是自定义事件类型,可以自定义。

  发布订阅事件模型本质上就是一个中央事件处理总线,它接收所有订阅的自定义事件,并将自定义事件的回调存储到事件回调的堆栈中,当某在某个时刻触发了此自定义事件,会调用分发事件的方法,从事件回调堆栈中取出符合条件的事件回调,依次调用,具体实现逻辑如下:

class EventEmiter {    constructor() {            //回调中心            this.listenerList = {};            this.createEventList = {};        }        /**         * 添加事件监听         * @param {事件类型} type         * @param {回调方法} fn         */    on(type, fn) {            if (!this.listenerList[type]) {                this.listenerList[type] = [];            }            this.listenerList[type].push(fn);        }        /**         * 触发事件监听         * @param {事件类型} type         */    emit(type, flag) {            let fnList = this.listenerList[type];            let params = Array.from(arguments);            if (!fnList || fnList.length === 0) {                return false;            } else {                fnList.forEach(fn => {                    fn.apply(this, params.slice(1));                });            }        }        /**         * 移除事件监听         * @param {事件类型} type         * @param {回调方法} fn         */    off(type, fn) {        if (!this.listenerList[type]) {            return false;        } else {            let index = this.listenerList[type].findIndex(vv => vv === fn);            if (index === -1) {                return false;            } else {                this.listenerList[type].splice(index, 1);            }        }    }}let eventBus = new EventEmiter();function cb(param) {    console.log("this is a test", param);}eventBus.on("test", cb);eventBus.emit("test", 123);eventBus.off("test", cb);eventBus.emit("test", 456);

  以上只是对发布订阅模式进行一个简单的实现,自定义事件只能分发给在触发前已订阅的消息,针对那些先触发,后订阅的内容,并不能得到一个很好的处理,所以,如果要解决这种弊端,就必须加一个离线的事件缓存。除此之外,发布订阅也有一些弊端,那就是每次发布消息,都会触发所有的事件监听回调,尽管大多数情况下并不想触发所有回调内容,所以在这种情况下,最好对事件加一些命名空间,以缩小其生效范围。

  以下为支持离线事件代码,只是对事件加了一个标记:

class EventEmitter {        constructor() {        //回调中心        this.listenerMap = {};        //离线事件列表        this.offlineListenerList = [];    }    /**     * 添加事件监听     * @param type 事件类型     * @param fn 回调函数     * @param flag 是否是离线事件     */    on(type, fn, flag) {        if (!this.listenerMap[type]) {            this.listenerMap[type] = [];        }        this.listenerMap[type].push(fn);        //如果注册了离线事件,则在监听事件时,需要检测是否有离线事件缓存        if (flag) {            let index = this.offlineListenerList.findIndex(vv => vv.type === type);            if (index !== -1) {                fn.call(this, this.offlineListenerList[index].params);                //清空该条离线事件记录                this.offlineListenerList.splice(index, 1);            }        }    }    /**     * 触发事件监听     * @param type 事件类型     * @param params 载荷参数     * @param flag     */    emit(type, params, flag) {        let fnList = this.listenerMap[type];        if (fnList && Array.isArray(fnList)) {            fnList.forEach(fn => {                fn.apply(this, params);            });        }        //如果注册的是离线事件,则吧        if (flag) {            this.offlineListenerList.push({                type,                params            });        }    }    /**     * 移除事件监听     */    off(type, fn) {        if (!this.listenerMap[type]) {            return false;        } else {            let index = this.listenerMap[type].findIndex(vv => vv === fn);            if (index === -1) {                return false;            } else {                this.listenerMap[type].splice(index, 1);            }        }    }        /**     * 只触发一次     * @param type     * @param fn     */    once(type, fn) {        let fnList = this.listenerMap[type];        let params = Array.from(arguments);        if (!fnList || fnList.length === 0) {            return false;        } else {            let index = fnList.findIndex( vv => vv === fn);            fnList[index].apply(this, params.slice(1));            fnList.splice(index, 1);        }    }}let event = new EventEmitter();event.emit('test', 1, true);event.on('test', params => {   console.log('offline', params)}, true);event.on('cc', () => {    console.log('normal', 22222);});event.emit('cc');

 

  

转载于:https://www.cnblogs.com/gerry2019/p/10241488.html

你可能感兴趣的文章
App右上角数字
查看>>
小算法
查看>>
201521123024 《java程序设计》 第12周学习总结
查看>>
新作《ASP.NET MVC 5框架揭秘》正式出版
查看>>
IdentityServer4-用EF配置Client(一)
查看>>
WPF中实现多选ComboBox控件
查看>>
读构建之法第四章第十七章有感
查看>>
Windows Phone开发(4):框架和页 转:http://blog.csdn.net/tcjiaan/article/details/7263146
查看>>
python asyncio 异步实现mongodb数据转xls文件
查看>>
TestNG入门
查看>>
【ul开发攻略】HTML5/CSS3菜单代码 阴影+发光+圆角
查看>>
IOS-图片操作集合
查看>>
IO—》Properties类&序列化流与反序列化流
查看>>
jquery实现限制textarea输入字数
查看>>
Codeforces 719B Anatoly and Cockroaches
查看>>
ActiveMQ与spring整合
查看>>
第一阶段冲刺06
查看>>
EOS生产区块:解析插件producer_plugin
查看>>
排球积分程序(三)——模型类的设计
查看>>
HDU 4635 Strongly connected
查看>>