PubSub的一种实现

今天在浏览JavaScript事件时,复习了下Dean Edward大神的addEvent。忽然以为能够基于他的思路实现一个结构更好的PubSub。app

思路也很简单,就是要维护一个相似以下的一个仓库结构:函数

/*
{
    'sayHello': {
        0: fn0,
            1: fn1,
        //...

    },
    'sayGoodBye': {
        0: fnn,
        //...
    },
    //...
}*/

下面是个人实现代码:测试

(function(exports) {
    var PubSub = exports.PubSub || {};

    //在PubSub对象上增长静态域PubSubCache,用于保存subscribe相关数据

    /**
     * PubSub.PubSubCache仓库结构
     * {
     *     'sayHello': {
     *         0: fn0,
     *         1:fn1,
     *         //..。
     *     },
     *     'sayGoodBye': {
     *        //...
     *     }
     * }
     *
     */
    PubSub.PubSubCache = PubSub.PubSubCache || {$uid: 0};

    //PubSub有4个静态方法:subscribe, subscribeOne, unsubscribe, publish
    //PubSub不会与DOM元素有关系。这样publish也只能手动去触发了
    PubSub.subscribe = function(type, handler) {
        var cache = this.PubSubCache[type] || (this.PubSubCache[type] = {});
        handler.$uid = handler.$uid || this.PubSubCache.$uid++;

        //把回调放入仓库中
        cache[handler.$uid] = handler;
    };

    PubSub.unsubscribe = function(type, handler) {
        var counter = 0,$type, cache = this.PubSubCache[type];

        if(arguments.length === 1) {
            //直接删除此种类型的订阅对象
            if(!cache) return true;
            return !!this.PubSubCache[type] && (delete this.PubSubCache[type]);
        } else if(arguments.length === 2) {
            !!this.PubSubCache[type] && (delete this.PubSubCache[type][handler.$uid]);
        }

        //PubSubCahe仓库中某类型订阅为空,则要删除这个订阅对象
        for($type in cache) {
            counter++;
        }

        return !counter && (delete this.PubSubCache[type]);
    };

    PubSub.publish = function(type) {
        var cache = this.PubSubCache[type], key, oneFlag, tmp, context, args = [].slice.call(arguments);

        if(!cache) return;

        if(args.length === 1) {
            context = exports;
        } else {
            context = args[1];
        }

        //执行回调
        for(key in cache) {
            tmp = cache[key];
            //在发布消息时能够指定回调函数的上下文,同时还能够传入参数
            cache[key].apply(context, args.slice(1));
            tmp.one && this.unsubscribe(type, tmp);
        }
    };

    PubSub.subscribeOne = function(type, handler) {
        this.subscribe(type, handler);
        //给函数加一个只执行一次的标志
        handler.one = true;
    };

    exports.PubSub = PubSub;
})(window);
 

下面是测试代码:ui

var data = {name: 'haha', age:18};

var handler2 = function(data) {
    console.log('say.hello excuted! 2');
    console.log(this.name);
};

//订阅say.hello消息
PubSub.subscribe('say.hello', function(data) {
    console.log('say.hello excuted! 1');
});

//第二次订阅say.hello消息
PubSub.subscribe('say.hello', handler2);

//第三次订阅say.hello消息
PubSub.subscribe('say.hello', function(data) {
    console.log('say.hello excuted! 3');
    console.log(this.age);
});

//第四次增长一个只会执行一次的say.hello消息订阅
PubSub.subscribeOne('say.hello', function(data) {
    console.log('say.hello excuted! one');
});

/**
 * 发布say.hello消息类型
 * 输出:
 * say.hello excuted! 1
 * say.hello excuted! 2
 * haha
 * say.hello excuted! 3
 * 18
 * say.hello excuted! one
 */


PubSub.publish('say.hello', data);


//取消第二次订阅的say.hello类型
PubSub.unsubscribe('say.hello', handler2);


/**
 * 发布say.hello消息类型
 * 输出:
 * say.hello excuted! 1
 * say.hello excuted! 3
 * 18
 */

console.log('--------------------------------')
PubSub.publish('say.hello', data);

/**
 * 再次发布say.hello消息,不过此次除了传入执行上下文外,还要传入参数
 * 输出:
 * say.hello excuted! 1
 * say.hello excuted! 3
 * 18
 * say.hello excuted! has deliverd args
 * args123
 */

PubSub.subscribe('say.hello', function(data, args) {
    console.log('say.hello excuted! has deliverd args');
    console.log(args);
});

console.log('--------------------------------')
PubSub.publish('say.hello', data, 'args123');

小结:this

全局的PubSub对象有四个方法:spa

1. subscribe(type, handler) :增长一种类型的消息订阅。相似jQuery的bind();code

2. subscribeOne(type, handler):增长一种回调只会执行一次的消息订阅,相似jQuery的one()对象

3. unsubscribe(type, [handler]): 若是只传type,会删除全部的type消息订阅;传入了回调函数,则只删除那一个回调。相似jQuery的unbind()blog

4. publish(type):执行订阅。相似jQuery的trigger();事件

 

固然上面的功能Cowboy大神只用了只行代码就实现了(基于jQuery):

(function($) {
    //获得一个jQuery对象,以便使用其方法
    var o = $({});    
    
    //为jQuery对象增长静态的方法
    $.subscribe = function() {
        o.bind.apply(o, arguments);
    };
    
    $.unsubscribe = function() {
        o.unbind.apply(o, arguments);
    };
    
    $.publish = function() {
        o.trigger.apply(o, arguments);
    }
    
})(jQuery);
相关文章
相关标签/搜索