发布订阅模式(雏形)

初始化Event对象html

var initEvent = function(obj) {
    for(var i in event) {
        obj[i] = event[i];
    }
};

主代码:设计模式

var event = {
    list: [],
    listen: function(key, fn) {
        // 肯定监听的事件容器默认是一个空数组
        if(!this.list[key]) {
            this.list[key] = [];
        }
        // 订阅的消息添加到缓存列表中
        this.list[key].push(fn);
        // 链式调用
        return this 
    },
    trigger: function(){
        // 获取trigger 函数参数的第一个参数,即key键
        // 此时arguments 是trigger的参数类数组
        var key = Array.prototype.shift.call(arguments);
        // 拿到对应key的监听事件数组
        var fns = this.list[key];
        // 若是没有订阅过该消息的话,则返回
        if(!fns || fns.length === 0) {
            return;
        }
        for(var i = 0, fn;i < fns.length; i ++) {
            fn = fns[i]
            //逐个调用key键所对应监听事件数组函数
            // 此时arguments 一样也是trigger的参数类数组,只不过少了第一个参数
            // 将此参数传递给fn函数做为形参
            // this 也是fn的执行做用域
            fn.apply(this, arguments);
        }
    }
   };

调用执行:数组

// 新建一个好比小红的对象
    var shoeObj = {};
    // 初始化小红对象
    initEvent(shoeObj);
    // 小红同时订阅以下消息(链式调用) 
    shoeObj.listen('red',function(size, price){
        console.log("尺码是:"+size);  
        console.log('price是' +price)
    }).listen('block', function (size, price) {
        console.log("尺码是:"+size);  
        console.log('price是' +price)
    })
    
    shoeObj.trigger("red", 40, 500);

    shoeObj.trigger("block",42, 300);

订阅了消息后,咱们可能会remove掉消息,因此Event对象新增一个方法:缓存

// 略
remove : function(key, fn) {
       var fns = this.list[key]
       // 若是key对应的消息没有订阅过的话,则返回
       if(!fns) return
       // 若是没有传入具体的回调函数,表示须要取消key对应消息的全部订阅
       if(!fn) {
         fns.length = 0 // 或者this.list[key] = []
         // fns = []   
         //fns = [] 这样写后,实际this.list[key]中的回调数组
         //依然存在,由于初始fns指向this.list[key]这个数组(数组是一个引用类型)
         //fns = [],表明咱们将fns又指向了一个新的数组长度为空的引用数组,而这个
         // 新的引用数组 和this.list[key]这个引用数组是计算机里面占用两个不一样的
         // 堆栈。
       }else {
         for(var i = 0; i < fns.length; i ++) {
           var _fn = fns[i]
           if(_fn === fn) {
              fns.splice(i, 1) // 删除订阅者的回调函数
           }
         }
       }
    },
// 略

调用app

// 小红订阅以下消息 同时在red上面订阅了两个消息
    // 注意fn1 和fn2 这种写法,比较少见,实际fn1,fn2成为了一个全局变量
    // 在remove的时候,做为具体的参数传递
    shoeObj.listen('red',fn1 = function(size, price){
        console.log("尺码是1----" +size);  
        console.log('price是1----' +price)
    }).listen('red', fn2 = function(size, price){
        console.log("尺码是2----" +size);  
        console.log('price是2----' +price)
    })
    
    //remove 掉fn2
    shoeObj.remove('red', fn2)
    // 触发回调 此时只会回调fn1
    shoeObj.trigger("red", 40, 500);
    
    //若是remove 不传参数,就会将red中全部的监听所有remove掉
    shoeObj.remove('red')
    shoeObj.trigger("red", 40, 500);

结束语
发布订阅模式是js中36中设计模式中最多见的模式,也是很重要的设计模式。其实咱们在写项目逻辑代码的时候,无形中也运用了这个思想,最多见的是click触发回调。好比咱们定义一个方法,在定义的时候已经listen在一个对象上,或window对象上。在click触发的时候,回调此方法,从而触发函数。
发布订阅模式比较适合写封装插件,我认为拿来写业务逻辑代码,有点不太好用。固然这只是我本身的观点。
接下来,我准备用这个模式封装一个上传的组件。函数

附录(参考文献)this

  1. cn一篇博文
相关文章
相关标签/搜索