简单的写一个发布订阅器

发布-订阅模式在开发中的应用实际上是很普遍的,好比你们都知道的 Vue,使用数据驱动模板的改变,将咱们的双手从繁琐的 dom 操做中解放出来,稍微懂一些原理的同窗们都知道,其双向数据绑定就是经过数据劫持、发布-订阅和 dom 模板编译实现的,就算不了解这个的同窗,js 中的事件监听相信作前端开发的同窗都写过;前端

其实事件监听就是一个订阅的操做,当某个事件触发的时候,对该事件监听时传入的 callback 就会被执行,这就是一个完整的发布的-订阅流程;接下来咱们就来简单的写一下一个发布订阅器:数组

思路:

俗话说:磨刀不误砍柴工,在动手以前,缕清一下思路是有必要的,首先咱们要明确这个发布订阅器的功能,其中包括bash

基本功能:app

  • 可添加订阅者
  • 可触发消息通知某个订阅器执行其中的全部订阅者
  • 可删除指定订阅者

升级功能:dom

  • 可同时给多个订阅器添加订阅者
  • 可删除指定订阅器中的全部订阅者
  • 可指定同时删除多个订阅器或者直接清空全部订阅器
  • 可添加一次性订阅(该订阅者一旦被执行,就会在订阅器中移除)
  • 处理好 this 指向和实现链式调用
  • 添加错误判断和提醒机制

接下来咱们就分两版上代码:函数

基础版

/**
 * 发布订阅器构造函数
 */
var Publisher = (function() {
    function Publisher() {
        this._subs = {}; // 维护一个订阅器列表
    }
    
    /**
     * 添加订阅者
     * 若订阅者须要插入的订阅器不存在,则新建立一个
     * @param { string } type - 须要添加订阅者的订阅器名
     * @param { function } func - 订阅者
     */
    Publisher.prototype.addSub = function(type, func) {
        if(!this._subs[type]) this._subs[type] = [];
        this._subs[type].push(func);
    };
    
    /**
     * 发布通知
     * 通知指定订阅器执行其中的每一个订阅者
     * @param { string } type - 须要通知其发布消息的订阅器名
     */
    Publisher.prototype.notify = function(type) {
        if(!this._subs[type]) return;
        var args = Array.prototype.slice.call(arguments, 1);
        this._subs[type].forEach(function(item) {
            item.apply(this, args);
        }, this);
    };
    
    /**
     * 删除订阅者
     * @param { string } type - 指定操做的订阅器名
     * @param { function } func - 指定须要删除的订阅者
     */
    Publisher.prototype.destory = function(type, func) {
        this._subs[type].forEach(function(item, index, array) {
            (item === func) && array.splice(index, 1);
        }, this);
    };
    
    return Publisher;
}());

基础版就如上面的代码,以最简单的方式实现一个发布订阅器的基本功能(能订阅,并能接收消息,还能指定删除)this

升级版:

/**
 * 发布订阅器构造函数
 */
var Publisher = (function() {
    function Publisher() {
        this._subs = {};
    }
    
    /**
     * 添加订阅者
     * 若订阅者须要插入的订阅器不存在,则新建立一个
     * 若传入的订阅器名为一个数组,则遍历数组内的每一个订阅器添加订阅者
     * @param { string|Array<string> } type - 须要添加订阅者的订阅器名
     * @param { function } func - 订阅者
     * @returns { object } - 发布器实例,可实现链式调用
     */
    Publisher.prototype.addSub = function(type, func) {
        if(Array.isArray(type)) {
            type.forEach(function(item) {
                this.addSub(item, func);
            }, this);
        } else {
            (this._subs[type] || (this._subs[type] = [])).push(func);
        }
    
        return this;
    };
    
    /**
     * 添加单次订阅
     * 当该订阅者在被通知执行一次以后会从订阅器中移除
     * @param { string|Array<string> } type - 须要添加订阅者的订阅器名
     * @param { function } func - 订阅者
     * @returns { object } - 发布器实例,可实现链式调用
     */
    Publisher.prototype.once = function(type, func) {
        function onceAdd() {
            var args = Array.prototype.slice(arguments);
            this.destory(type, onceAdd);
            func.apply(this, args);
        }
        this.addSub(type, onceAdd);
    
        return this;
    };
    
    /**
     * 发布通知
     * 通知指定订阅器执行其中的每一个订阅者
     * @param { string } type - 须要通知其发布消息的订阅器名
     * @returns { object } - 发布器实例,可实现链式调用
     */
    Publisher.prototype.notify = function(type) {
        if(this._subs[type] === void 0) throw TypeError("Can't find the " + type + " event in the Publisher");
        
        var args = Array.prototype.slice.call(arguments, 1);
        this._subs[type].forEach(function(item) {
            item.apply(this, args);
        }, this);
    
        return this;
    };
    
    /**
     * 删除订阅器/订阅者
     * 若没有传入参数直接调用方法,将清空整个订阅器列表
     * 若只传入了订阅器类型参数,没有指定删除的订阅者,则删除该订阅器
     * @param { string|Array<string> } type - 指定操做的订阅器名,可用数组传入多个须要操做的订阅器名
     * @param { function } func - 指定须要删除的订阅者
     * @return { object } - 发布器实例,可实现链式调用
     */
    Publisher.prototype.destory = function(type, func) {
        if(func && typeof func !== 'function') throw TypeError('The param "func" should be a function!');
    
        if(!arguments.length) {
            this._subs = {};
            return this;
        }
        if(Array.isArray(type)) {
            type.forEach(function(item) {
                this.destory(item, func);
            }, this);
        }
        if(!func) {
            delete this._subs[type];
        } else {
            this._subs[type].forEach(function(item, index, array) {
                (item === func) && array.splice(index, 1);
            }, this);
        }
        return this;
    };
    
    return Publisher;
}());

以上就是一个简单的发布订阅器了,如有什么不足的地方,还望各位同窗指正!spa

相关文章
相关标签/搜索