javascript设想形式进修笔记之宣布-定阅形式

宣布-定阅形式

  • 定义对象间的一种 一对多 的依靠关联, 当一个对象的状况发作转变时, 一切依靠于它的对象都将获得关照
  • 简朴完成
    // 定义宣布者
    var salesOffices = {};

    // 缓存列表, 寄存定阅者的回调函数
    salesOffices.clientList = [];
    
    // 定义定阅者
    salesOffices.listen = function (fn) {
        this.clientList.push(fn);
    }

    // 宣布音讯
    salesOffices.trigger = function () {
        for (var i = 0, fn; fn = this.clientList[i++];) {
            fn.apply(this, arguments)
        }
    }
    
    /*** 测试 ***/
    
    // 定阅者1
    salesOffices.listen(function (price, squareMeter) {
        console.log('价钱=' + price);
        console.log('squareMeter= ' + squareMeter);
    });

    // 定阅者2
    salesOffices.listen(function (price, squareMeter) {
        console.log('价钱=' + price);
        console.log('squareMeter= ' + squareMeter);
    });

    // 宣布音讯
    salesOffices.trigger(2000, 80);
    salesOffices.trigger(3000, 100);
  • 上面的完成体式格局, 致使了, 每一个定阅者都邑收到宣布者宣布的音讯
    // 定义宣布者
    var salesOffices = {};

    // 缓存列表, 寄存定阅者的回调函数
    salesOffices.clientList = {};

    // 定义定阅者 (增添缓存, 增添标示 key )
    salesOffices.listen = function (key, fn) {
        if (!this.clientList[key]) {
            this.clientList[key] = [];
        }
        this.clientList[key].push(fn);
    }

    // 宣布音讯
    salesOffices.trigger = function () {

        var key = Array.prototype.shift.call(arguments),
            fns = this.clientList[key];

        if (!fns || fns.length === 0) {
            return false;
        }

        for (var i = 0, fn; fn = fns[i++];) {
            fn.apply(this, arguments)
        }
    }

提炼宣布-定阅形式


    // 中心事宜
    var event = {
        clientList: {},
        listen: function (key, fn) {
            if (!this.clientList[key]) {
                this.clientList[key] = [];
            }
            this.clientList[key].push(fn);
        },
        trigger: function () {
            var key = Array.prototype.shift.call(arguments),
                fns = this.clientList[key];

            if (!fns || fns.length === 0) {
                return false
            }

            for (var i = 0, fn; fn = fns[i++];) {
                fn.apply(this, arguments);
            }
        }
    }
    
    // 作废定阅音讯
    event.remove = function (key, fn) {
        var fns = this.clientList[key];

        if (!fns) {
            return false;
        }

        if (!fn) {
            // 没有传入fn 作废key对应的一切的定阅
            fns && (fns.length = 0);
        } else {
            // 传入fn 删除对应的fn
            for (var l = fns.length - 1; l >= 0; l--) {
                var _fn = fns[l];
                if (_fn === fn) {
                    fns.splice(l, 1)
                }
            }
        }
    }
    
    // 给任何对象动态增添宣布-定阅功用
    var installEvent = function (obj) {
        for (var key in event) {
            if (event.hasOwnProperty(key)) {
                obj[key] = event[key];
            }
        }
    }
    
    /*** 测试 ***/
    var salesOffices = {};

    installEvent(salesOffices);

    // 定阅者1
    salesOffices.listen('squareMeter80', function (price) {
        console.log('价钱=' + price);
    });

    // 定阅者2
    salesOffices.listen('squareMeter100', function (price) {
        console.log('价钱=' + price);
    });

    // 宣布音讯
    salesOffices.trigger('squareMeter80', 20000);
    salesOffices.trigger('squareMeter100', 30000);
  • 登录案例

    // 登录胜利, 宣布胜利音讯
    $.ajax('http://xxx.com/login', function (data) {
        login.trigger('loginSuccess', data);
    });

    // 这类写法也很好
    var header = (function () {
        // 定阅音讯
        login.listen('loginSuccess', function (data) {
            header.setAvatar(data);
        })
        return {
            setAvatar: function (data) {
                console.log('header 模块拿到用户信息')
            }
        }
    })();
  • 以上写法会有三个题目

    • 须要一个相似’中介者’的角色, 把宣布者和定阅者联络起来(经由过程 全局的 Event 来处理)
    • 以上必须先定阅, 才宣布
    • 全局定名的争执

完成相似 Event 的宣布-定阅形式

  • 长处1: 时候上的解耦,
  • 长处2: 对象之间的解耦
  • 瑕玷1: 建立定阅者本生要斲丧内存和时候
  • 瑕玷2: 弱化了对象之间的联络以后, 对象之间的必要联络也被湮没

    var Event = (function() {
        var global = this,
            Event,
            _default = 'default';

        Event = function () {
            var _listen,
                _trigger,
                _remove,
                _slice = Array.prototype.slice,
                _shift = Array.prototype.shift,
                _unshift = Array.prototype.unshift,
                namespaceCache = {},
                _create,
                find,
                each = function(arr, fn) {
                    var ret;
                    for (var i = 0, l = arr.length; i < l; i++) {
                        var n = arr[i];
                        ret = fn.call(n, i, n);
                    }
                    return ret;
                };

            _listen = function (key, fn, cache) {
                if (!cache[key]) {
                    cache[key] = [];
                }
                cache[key].push(fn);
            };

            _remove = function (key, cache, fn) {
                if (cache[eky]) {
                    if (fn) {
                        for (var i = cache[key].length - 1; i >= 0; i--) {
                            if (cache[key][i] === fn) {
                                cache[key].splice(i, 1);
                            }
                        }
                    } else {
                        cache[key] = [];
                    }
                }
            }

            _trigger = function () {
                var cache = _shift.call(arguments),
                    key = _shift.call(arguments),
                    args = arguments,
                    _self = this,
                    ret,
                    stack = cache[key];

                if (!stack || !stack.length) {
                    return;
                }

                return each(stack, function () {
                    return this.apply(_self, args);
                })
            };

            _create = function (namespace) {
                namespace = namespace || _default;
                var cache = {},
                    offlineStack = [],
                    ret = {
                        listen: function (key, fn, last) {
                            _listen(key, fn, cache);

                            if (offlineStack === null) {
                                return;
                            }

                            if (last === 'last') {
                                offlineStack.length && offlineStack.pop()();
                            } else {
                                each(offlineStack, function () {
                                    this();
                                })
                            }

                            offlineStack = null;
                        },

                        one: function (key, fn, last) {
                            _remove(key, fn, cache);
                            this.listen(key, fn, last);
                        },

                        remove: function (key, fn) {
                            _remove(key, cache, fn);
                        },

                        trigger: function () {
                            var fn,
                                args,
                                _self = this;

                            _unshift.call(arguments, cache);

                            args = arguments;

                            fn = function () {
                                return _trigger.apply(_self, args);
                            }

                            if (offlineStack) {
                                return offlineStack.push(fn);
                            }

                            return fn();
                        }
                    };

                return  namespace
                            ? (namespaceCache[namespace]
                                ? namespaceCache[namespace]
                                : namespaceCache[namespace] = ret)
                            : ret
            }

            return {
                create: _create,
                one: function (key, fn, last) {
                    var event = this.create();
                    event.one(key, fn, last);
                },
                remove: function (key, fn) {
                    var event = this.create();
                    event.remove(key, fn);
                },
                listen: function (key, fn, last) {
                    var event = this.create();
                    event.listen(key, fn, last);
                },
                trigger: function () {
                    var event = this.create();
                    event.trigger.apply(this, arguments);
                }
            }
        }()

        return Event;
    })();

视察者(observer) 形式 和 宣布/定阅形式 之间的区分

  • 本质上的区分实时在调理的处所差别
  • 分清晰谁是宣布者, 谁是视察者, 谁是定阅者
  • 视察者形式
    // subject(宣布) 中的目的发作变化. Observer(视察) 能接遭到变化
    function ObserverList() {
        this.observerList = [];
    }

    ObserverList.prototype.add = function (obj) {
        return this.observerList.push(obj);
    }
    
    ObserverList.prototype.get = function (index) {
        if (index > -1 && this.observerList.length) {
            return this.observerList[index];
        }
    }

    ObserverList.prototype.indexOf = function (obj, startIndex) {
        var i = startIndex;

        while(i < this.observerList.length) {
            if (this.observerList[i] === obj) {
                return i;
            }
            i++;
        }

        return -1;
    }
    
    ObserverList.prototype.removeAt = function (index) {
        this.observerList.splice(index, 1);
    }
    
    // 宣布者
    function Subject() {
        this.observers = new ObserverList();
    }

    Subject.prototype.addObserver = function (observer) {
        this.observers.add(observer);
    }

    Subject.prototype.removeObserver = function (observer) {
        this.observers.removeAt(this.observers.indexOf(observer, 0));
    }

    Subject.prototype.notify = function (context) {
        var observerCount = this.observers.count();
        for (var i = 0; i < observerCount; i++) {
            this.observers.get(i).update(context);
        }
    }

    // 视察者
    function Observer() {
        this.update = function () {
            // ...
        }
    }
  • 宣布(Publish)/定阅(Subscribe)形式

    var pubsub = {};
    (function (myObject) {
        var topics = {};

        var subUid = -1;

        // 宣布
        myObject.publish = function (topic, args) {
            if (!topics[topic]) {
                return false;
            }

            var subscribers = topics[topic],
                len = subscribers ? subscribers.length : 0;

            while (len--) {
                subscribers[len].func(topic, args);
            }

            return this;
        }

        // 定阅者
        myObject.subscribe = function (topic, func) {
            if (!topics[topic]) {
                topics[topic] = [];
            }

            var token = (++subUid).toString();

            topics[topic].push({
                token: token,
                func: func
            })
        }

        // 作废定阅
        myObject.unsubscribe = function (token) {
            for (var m in topics) {
                if (topics[m]) {
                    for (var i = 0, j = topics[m].length; i < j; i++) {
                        if (topics[m][i].token === token) {
                            topics[m].splice(i, 1);
                            return token;
                        }
                    }
                }
            }
            return this;
        }
    })(pubsub)
    原文作者:SonLior
    原文地址: https://segmentfault.com/a/1190000019418876
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞