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);
相關文章
相關標籤/搜索