snowshoes
4/13/2018 - 8:15 AM

订阅-发布模式(观察者模式)

订阅-发布模式(观察者模式)

var EventTarget = (function() {
    var _toArray = (function(obj, offset) {
        offset = offset >= 0 ? offset : 0;
        if (Array.from) {
            // Array.from: convert an obj or an array-like obj to an array
            return function(obj, offset) {
                return Array.from(obj).slice(offset);
            }
        }

        return function(obj, offset) {
            return Array.prototype.slice.call(obj, offset);
        }
    })();

    // var a = EventTarget({id: 1})
    // var a = new EventTarget({id: 1})
    function EventTarget(obj, isSimple) {
        // 没有使用 new 操作符调用时
        if (!this instanceof EventTarget) {
            return new EventTarget(obj);
        }
        if (this instanceof EventTarget) {
            if (obj !== null && obj !== undefined) {
                for (var prop in obj) {
                    if (obj.hasOwnProperty(prop)) {
                        this[prop] = obj[prop];
                    }
                }
            }
            this._handlers = {};
        }
    }

    EventTarget.prototype = {
        constructor: EventTarget,

        _get: function(eventName) {
            return this._handlers[eventName] || (this._handlers[eventName] = {on: [], one: []});
        },

        // 订阅
        on: function(eventName, handler) {
            let events = this._get(eventName);
            // 同一个消息一次性增加多个订阅者
            if (Array.isArray(handler)) {
                for (var i = 0, len = handler.length; i < len; i++) {
                    if (typeof handler[i] === 'function') {
                        events.on.push(handler[i]);
                    }
                }
                return this;
            }
            if (typeof handler !== 'function') {
                return this;
            }
            this._handlers[eventName].on.push(handler);
            return this;
        },

        // 订阅的另一种写法
        subscribe: function(eventNotification, handler) {
            return this.on(eventNotification, handler);
        },

        one: function(eventName, handler) {
            let events = this._get(eventName);

            // 同一个消息一次性增加多个订阅者
            if (Array.isArray(handler)) {
                for (var i = 0, len = handler.length; i < len; i++) {
                    if (typeof handler[i] === 'function') {
                        events.one.push(handler[i]);
                    }
                }
                return this;
            }
            if (typeof handler !== 'function') {
                return this;
            }
            this._handlers[eventName].one.push(handler);
            return this;
        },

        first: function(eventName, handler) {
            if (!handler || typeof handler !== 'function') {
                return this;
            }
            this._get(eventName).first = handler;
            return this;
        },

        last: function(eventName, handler) {
            if (!handler || typeof handler !== 'function') {
                return this;
            }
            this._get(eventName).last = handler;
            return this;
        },

        // 消息通知
        emit: function(eventName, data) {
            var eventObject = this._get(eventName);

            eventObject.first && eventObject.first({
                name: eventName,
                type: 'first',
                data: data
            });

            var oneArr = eventObject.one;
            while (oneArr.length) {
                oneArr.shift()({
                    name: eventName,
                    type: "one",
                    data: data
                });
            }

            for (var i = 0, len = eventObject.on.length; i < len; i++) {
                eventObject.on[i]({
                    name: eventName,
                    type: 'on',
                    data: data
                });
            }

            eventObject.last && eventObject.last({
                name: eventName,
                type: 'last',
                data: data
            });

            return this;
        },

        // 消息通知的另一种写法
        dispatch: function(eventName, data) {
            return this.emit(eventName, data);
        },

        // 消息通知的再一种写法
        trigger: function(eventName, data) {
            return this.emit(eventName, data);
        },

        clear: function(eventName) {
            if (eventName in this._handlers) {
                this._handlers[eventName] = {on: [], one: []};
            }

            return this;
        },

        empty: function() {
            this._handlers = {};
            return this;
        }
    };

    return EventTarget;
})();