zoukankan      html  css  js  c++  java
  • PubSub的一种实现

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

    思路也很简单,就是要维护一个类似如下的一个仓库结构:

    /*
    {
        '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);
     

    下面是测试代码:

    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');

    小结:

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

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

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

    3. unsubscribe(type, [handler]): 如果只传type,会删除所有的type消息订阅;传入了回调函数,则只删除那一个回调。类似jQuery的unbind()

    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);
  • 相关阅读:
    PHP5.6 和PHP7.0区别
    怎么清除火狐浏览器的cookie?
    PHP 7.0新增特性详解
    一个较好的基础的数据库连接池知识 规格严格
    Iptables 规格严格
    收藏一个Man网址 规格严格
    AIX配置SNMP V3 规格严格
    Tomcat Firewall JMX RMI 规格严格
    在来一个IPTables 规格严格
    java.lang.ClassNotFoundException: listeners.ContextListener . 规格严格
  • 原文地址:https://www.cnblogs.com/jagusOu/p/3855388.html
Copyright © 2011-2022 走看看