diff --git a/packages/core/event-emitter.js b/packages/core/event-emitter.js index 56e669e..bd564dc 100644 --- a/packages/core/event-emitter.js +++ b/packages/core/event-emitter.js @@ -1,6 +1,6 @@ -function createListener(fn, that, type, namespace, once) { +function createListener(fn, that, type, once) { return { - fn, that, type, namespace, once, + fn, that, type, once, bound: that ? fn.bind(that) : fn, } } @@ -11,73 +11,46 @@ export function EventEmitterMixin(Superclass) { constructor(...args) { super(...args); - this.$$events = {}; - this.$$namespaces = {}; + this.$$cachedListenerLookup = () => {} + this.$$events = Object.create(null); } addListener(...args) { return this.on(...args); } // Notify ALL the listeners! - emit(...args) { - const type = args[0]; - const listeners = this.lookupEmitListeners(type); - if (0 === listeners.length) { - return; + emit(type, ...args) { + const typeListeners = this.$$events[type]; + if (typeListeners && typeListeners.length > 0) { + this.emitToListeners(typeListeners, args); } + } + emitToListeners(listeners, args) { for (let i = 0; i < listeners.length; ++i) { - const {once, type, namespace, fn, bound, that} = listeners[i]; - const offset = type !== '*' ? 1 : 0; - + const {once, type, fn, bound, that} = listeners[i]; + // Remove if only once. if (once) { - this.removeListener(`${type}.${namespace}`, fn); + this.offSingleEvent(type, fn); } - // Fast path... - if (args.length === offset) { + if (0 === args.length) { bound() } - - else if (args.length === offset + 1) { - bound( - args[offset + 0] - ); + else if (1 === args.length) { + bound(args[0]); } - - else if (args.length === offset + 2) { - bound( - args[offset + 0], - args[offset + 1] - ) + else if (2 === args.length) { + bound(args[0], args[1]); } - - else if (args.length === offset + 3) { - bound( - args[offset + 0], - args[offset + 1], - args[offset + 2] - ) + else if (3 === args.length) { + bound(args[0], args[1], args[2]); } - - else if (args.length === offset + 4) { - bound( - args[offset + 0], - args[offset + 1], - args[offset + 2], - args[offset + 3] - ) + else if (4 === args.length) { + bound(args[0], args[1], args[2], args[3]); } - - else if (args.length === offset + 5) { - bound( - args[offset + 0], - args[offset + 1], - args[offset + 2], - args[offset + 3], - args[offset + 4] - ) + else if (5 === args.length) { + bound(args[0], args[1], args[2], args[3], args[4]); } - // Slow path... else { fn.apply(that, args.slice(offset)); @@ -85,101 +58,48 @@ export function EventEmitterMixin(Superclass) { } } - lookupEmitListeners(type) { - return ['*', type].reduce((r, type) => { - return r.concat(type in this.$$events ? this.$$events[type] : []); - }, []); - } - off(typesOrType, fn) { - parseTypes(typesOrType).forEach((typeOrCompositeType) => { - this.offSingleEvent(typeOrCompositeType, fn); - }); + typesOrType = Array.isArray(typesOrType) ? typesOrType : [typesOrType]; + for (let i = 0; i < typesOrType.length; i++) { + const type = typesOrType[i]; + this.offSingleEvent(type, fn); + } return this; } - offSingleEvent(typeOrCompositeType, fn) { - const [type, namespace] = decomposeType(typeOrCompositeType); - - // Function. - if ('function' === typeof fn) { - const lists = []; - - if ((type in this.$$events)) { - lists.push(this.$$events); - } - - if ( - (namespace in this.$$namespaces) - && (type in this.$$namespaces[namespace]) - ) { - lists.push(this.$$namespaces[namespace]); - } - - lists.forEach((listeners) => { - listeners[type] = listeners[type].filter((listener) => { - return listener.fn !== fn; - }); - if (0 === listeners[type].length) { - delete listeners[type]; - } - }); - - if ( - (namespace in this.$$namespaces) - ) { - if (0 === Object.keys(this.$$namespaces[namespace]).length) { - delete this.$$namespaces[namespace]; - } - } - - return; - } - - // Only type. - if (0 === namespace.length) { - + offSingleEvent(type, fn) { + if ('function' !== typeof fn) { + // Only type. if (type in this.$$events) { delete this.$$events[type]; } - - for (const namespace in this.$$namespaces) { - const namespaceEvents = this.$$namespaces[namespace]; - if (type in namespaceEvents) { - delete namespaceEvents[type]; - } - } - return; } - - // Only namespace. - if (!(namespace in this.$$namespaces)) { - return; + // Function. + const lists = []; + if ((type in this.$$events)) { + lists.push(this.$$events); } - if (0 === type.length) { - for (const type in this.$$namespaces[namespace]) { - this.removeEventListenersFor(type, namespace); - } - delete this.$$namespaces[namespace]; - } - - // Type & namespace. - else if (type in this.$$namespaces[namespace]) { - this.removeEventListenersFor(type, namespace); - delete this.$$namespaces[namespace][type]; + const listeners = this.$$events; + listeners[type] = listeners[type].filter((listener) => { + return listener.fn !== fn; + }); + if (0 === listeners[type].length) { + delete listeners[type]; } } - on(types, fn, that = undefined) { - this._on(types, fn, that, false); + on(typesOrType, fn, that = undefined) { + this._on(typesOrType, fn, that, false); return this; } _on(typesOrType, fn, that, once) { - parseTypes(typesOrType).forEach((typeOrCompositeType) => { - this.onSingleEvent(typeOrCompositeType, fn, that, once); - }); + typesOrType = Array.isArray(typesOrType) ? typesOrType : [typesOrType]; + for (let i = 0; i < typesOrType.length; i++) { + const type = typesOrType[i]; + this.onSingleEvent(type, fn, that, once); + } } once(types, fn, that = undefined) { @@ -187,66 +107,17 @@ export function EventEmitterMixin(Superclass) { return this; } - onSingleEvent(typeOrCompositeType, fn, that, once) { - const [type, namespace] = decomposeType(typeOrCompositeType); - const listener = createListener(fn, that, type, namespace, once); - + onSingleEvent(type, fn, that, once) { + const listener = createListener(fn, that, type, once); if (!(type in this.$$events)) { this.$$events[type] = []; } this.$$events[type].push(listener); - - if (!(namespace in this.$$namespaces)) { - this.$$namespaces[namespace] = {}; - } - if (!(type in this.$$namespaces[namespace])) { - this.$$namespaces[namespace][type] = []; - } - this.$$namespaces[namespace][type].push(listener); } - removeEventListenersFor(type, namespace) { - - for (const {fn} of this.$$namespaces[namespace][type]) { - this.$$events[type] = this.$$events[type].filter((listener) => { - return listener.fn !== fn; - }); - } - - if (0 === this.$$events[type].length) { - delete this.$$events[type]; - } + removeListener(...args) { + return this.off(...args); } - removeListener(...args) { return this.off(...args); } - } } - -export function decomposeType(typeOrCompositeType) { - const index = typeOrCompositeType.indexOf('.'); - const isCompositeType = -1 !== index; - if (isCompositeType) { - return [ - typeOrCompositeType.substr(0, index), - typeOrCompositeType.substr(index + 1), - ]; - } - return [typeOrCompositeType, '']; -} - -// Split, flatten, and trim. -export function parseTypes(typesOrType) { - const types = Array.isArray(typesOrType) ? typesOrType : [typesOrType]; - return types.map((type) => { - return type.split(' '); - - }).reduce((r, types) => { - r.push(...types); - return r; - - }, []).map((type) => { - return type.trim(); - - }); -}