perf: optimization & simplification
This commit is contained in:
parent
ad7b03ee35
commit
353d1aec5d
|
@ -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);
|
||||
|
||||
offSingleEvent(type, fn) {
|
||||
if ('function' !== typeof fn) {
|
||||
// Only type.
|
||||
if (type in this.$$events) {
|
||||
delete this.$$events[type];
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 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) => {
|
||||
const listeners = this.$$events;
|
||||
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) {
|
||||
|
||||
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;
|
||||
}
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
removeListener(...args) {
|
||||
return this.off(...args);
|
||||
}
|
||||
|
||||
if (0 === this.$$events[type].length) {
|
||||
delete this.$$events[type];
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user