Skip to content

Instantly share code, notes, and snippets.

@Olical
Created August 15, 2012 21:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Olical/3364020 to your computer and use it in GitHub Desktop.
Save Olical/3364020 to your computer and use it in GitHub Desktop.
Snapshot of EventEmitter 3 and 4 to be used in jsPerf: Test case URL will be http://jsperf.com/eventemitter-3-vs-4
/**
* EventEmitter v3.1.7
* https://github.com/Wolfy87/EventEmitter
*
* Oliver Caldwell (http://oli.me.uk)
* Creative Commons Attribution 3.0 Unported License (http://creativecommons.org/licenses/by/3.0/)
*/
;(function(exports) {
// JSHint config
/*jshint devel:true*/
/*global define:true*/
// Place the script into strict mode
'use strict';
/**
* EventEmitter class
* Creates an object with event registering and firing methods
*/
function EventEmitter() {
// Initialise required storage variables
this._events = {};
this._maxListeners = 10;
}
/**
* Event class
* Contains Event methods and property storage
*
* @param {String} type Event type name
* @param {Function} listener Function to be called when the event is fired
* @param {Object} scope Object that this should be set to when the listener is called
* @param {Boolean} once If true then the listener will be removed after the first call
* @param {Object} instance The parent EventEmitter instance
*/
function Event(type, listener, scope, once, instance) {
// Store arguments
this.type = type;
this.listener = listener;
this.scope = scope;
this.once = once;
this.instance = instance;
}
/**
* Executes the listener
*
* @param {Array} args List of arguments to pass to the listener
* @return {Boolean} If false then it was a once event
*/
Event.prototype.fire = function(args) {
this.listener.apply(this.scope || this.instance, args);
// Remove the listener if this is a once only listener
if(this.once) {
// By storing the original value we can make sure it was actually removed
// There was a bug in which you would get an infinite loop if you called removeAllListeners from a once event
// By doing this if the event is lost it will not make the loop go back one
var original = this.instance.listeners(this.type).length;
this.instance.removeListener(this.type, this.listener, this.scope);
// Only return false if the length has changed
// This is a fix for issue #26
if(original !== this.instance.listeners(this.type).length) {
return false;
}
}
};
/**
* Passes every listener for a specified event to a function one at a time
*
* @param {String} type Event type name
* @param {Function} callback Function to pass each listener to
* @return {Object} The current EventEmitter instance to allow chaining
*/
EventEmitter.prototype.eachListener = function(type, callback) {
// Initialise variables
var i = null,
possibleListeners = null,
result = null;
// Only loop if the type exists
if(this._events.hasOwnProperty(type)) {
possibleListeners = this._events[type];
for(i = 0; i < possibleListeners.length; i += 1) {
result = callback.call(this, possibleListeners[i], i);
if(result === false) {
i -= 1;
}
else if(result === true) {
break;
}
}
}
// Return the instance to allow chaining
return this;
};
/**
* Adds an event listener for the specified event
*
* @param {String} type Event type name
* @param {Function} listener Function to be called when the event is fired
* @param {Object} scope Object that this should be set to when the listener is called
* @param {Boolean} once If true then the listener will be removed after the first call
* @return {Object} The current EventEmitter instance to allow chaining
*/
EventEmitter.prototype.addListener = function(type, listener, scope, once) {
// Create the listener array if it does not exist yet
if(!this._events.hasOwnProperty(type)) {
this._events[type] = [];
}
// Push the new event to the array
this._events[type].push(new Event(type, listener, scope, once, this));
// Emit the new listener event
this.emit('newListener', type, listener, scope, once);
// Check if we have exceeded the maxListener count
// Ignore this check if the count is 0
// Also don't check if we have already fired a warning
if(this._maxListeners && !this._events[type].warned && this._events[type].length > this._maxListeners) {
// The max listener count has been exceeded!
// Warn via the console if it exists
if(typeof console !== 'undefined') {
console.warn('Possible EventEmitter memory leak detected. ' + this._events[type].length + ' listeners added. Use emitter.setMaxListeners() to increase limit.');
}
// Set the flag so it doesn't fire again
this._events[type].warned = true;
}
// Return the instance to allow chaining
return this;
};
/**
* Alias of the addListener method
*
* @param {String} type Event type name
* @param {Function} listener Function to be called when the event is fired
* @param {Object} scope Object that this should be set to when the listener is called
* @param {Boolean} once If true then the listener will be removed after the first call
*/
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
/**
* Alias of the addListener method but will remove the event after the first use
*
* @param {String} type Event type name
* @param {Function} listener Function to be called when the event is fired
* @param {Object} scope Object that this should be set to when the listener is called
* @return {Object} The current EventEmitter instance to allow chaining
*/
EventEmitter.prototype.once = function(type, listener, scope) {
return this.addListener(type, listener, scope, true);
};
/**
* Removes the a listener for the specified event
*
* @param {String} type Event type name the listener must have for the event to be removed
* @param {Function} listener Listener the event must have to be removed
* @param {Object} scope The scope the event must have to be removed
* @return {Object} The current EventEmitter instance to allow chaining
*/
EventEmitter.prototype.removeListener = function(type, listener, scope) {
this.eachListener(type, function(currentListener, index) {
// If this is the listener remove it from the array
// We also compare the scope if it was passed
if(currentListener.listener === listener && (!scope || currentListener.scope === scope)) {
this._events[type].splice(index, 1);
}
});
// Remove the property if there are no more listeners
if(this._events[type] && this._events[type].length === 0) {
delete this._events[type];
}
// Return the instance to allow chaining
return this;
};
/**
* Alias of the removeListener method
*
* @param {String} type Event type name the listener must have for the event to be removed
* @param {Function} listener Listener the event must have to be removed
* @param {Object} scope The scope the event must have to be removed
* @return {Object} The current EventEmitter instance to allow chaining
*/
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
/**
* Removes all listeners for a specified event
* If no event type is passed it will remove every listener
*
* @param {String} type Event type name to remove all listeners from
* @return {Object} The current EventEmitter instance to allow chaining
*/
EventEmitter.prototype.removeAllListeners = function(type) {
// Check for a type, if there is none remove all listeners
// If there is a type however, just remove the listeners for that type
if(type && this._events.hasOwnProperty(type)) {
delete this._events[type];
}
else if(!type) {
this._events = {};
}
// Return the instance to allow chaining
return this;
};
/**
* Retrieves the array of listeners for a specified event
*
* @param {String} type Event type name to return all listeners from
* @return {Array} Will return either an array of listeners or an empty array if there are none
*/
EventEmitter.prototype.listeners = function(type) {
// Return the array of listeners or an empty array if it does not exist
if(this._events.hasOwnProperty(type)) {
// It does exist, loop over building the array
var listeners = [];
this.eachListener(type, function(evt) {
listeners.push(evt.listener);
});
return listeners;
}
return [];
};
/**
* Emits an event executing all appropriate listeners
* All values passed after the type will be passed as arguments to the listeners
*
* @param {String} type Event type name to run all listeners from
* @return {Object} The current EventEmitter instance to allow chaining
*/
EventEmitter.prototype.emit = function(type) {
// Calculate the arguments
var args = [],
i = null;
for(i = 1; i < arguments.length; i += 1) {
args.push(arguments[i]);
}
this.eachListener(type, function(currentListener) {
return currentListener.fire(args);
});
// Return the instance to allow chaining
return this;
};
/**
* Sets the max listener count for the EventEmitter
* When the count of listeners for an event exceeds this limit a warning will be printed
* Set to 0 for no limit
*
* @param {Number} maxListeners The new max listener limit
* @return {Object} The current EventEmitter instance to allow chaining
*/
EventEmitter.prototype.setMaxListeners = function(maxListeners) {
this._maxListeners = maxListeners;
// Return the instance to allow chaining
return this;
};
/**
* Builds a clone of the prototype object for you to extend with
*
* @return {Object} A clone of the EventEmitter prototype object
*/
EventEmitter.extend = function() {
// First thing we need to do is create our new prototype
// Then we loop over the current one copying each method out
// When done, simply return the clone
var clone = {},
current = this.prototype,
key = null;
for(key in current) {
// Make sure this is actually a property of the object before copying it
// We don't want any default object methods leaking though
if(current.hasOwnProperty(key)) {
clone[key] = current[key];
}
}
// All done, return the clone
return clone;
};
// Export the class
// If AMD is available then use it
if(typeof define === 'function' && define.amd) {
define(function() {
return EventEmitter;
});
}
// No matter what it will be added to the global object
exports.EventEmitter3 = EventEmitter;
}(this));
/**
* EventEmitter v4.0.0 - github.com/Wolfy87/EventEmitter
* Oliver Caldwell
* MIT license
*/
;(function(exports) {
// JSHint config - http://www.jshint.com/
/*global define:true*/
// Place the script in strict mode
'use strict';
/**
* Class for managing events.
* Can be extended to provide event functionality in other classes.
*
* @class Manages event registering and emitting.
*/
function EventEmitter(){}
/**
* Provides a shortcut to the prototype.
*
* @property {Object} Shortcut to the prototype object.
*/
EventEmitter.fn = EventEmitter.prototype;
/**
* Returns the listener array for the specified event.
* Will initialise the event object and listener arrays if required.
*
* @param {String} evt Name of the event to return the listeners from.
* @returns {Function[]} All listener functions for the event.
*/
EventEmitter.fn.getListeners = function(evt) {
// Create a shortcut to the storage object
// Initialise it if it does not exists yet
var events = this._events || (this._events = {});
// Return the listener array
// Initialise it if it does not exist
return events[evt] || (events[evt] = []);
};
/**
* Finds the index of the listener for the event in it's storage array
*
* @param {Function} listener Method to look for.
* @param {Function[]} listeners Array of listeners to search through.
* @returns {Number} Index of the specified listener, -1 if not found
*/
EventEmitter.fn.indexOfListener = function(listener, listeners) {
// Return the index via the native method if possible
if(listeners.indexOf) {
return listeners.indexOf(listener);
}
// There is no native method
// Use a manual loop to find the index
var i = listeners.length;
while(i--) {
// If the listener matches, return it's index
if(listeners[i] === listener) {
return i;
}
}
// Default to returning -1
return -1;
};
/**
* Adds a listener function to the specified event.
* The listener will not be added if it is a duplicate.
*
* @param {String} evt Name of the event to attach the listener to.
* @param {Function} listener Method to be called when the event is emitted.
* @returns {Object} Current instance of EventEmitter for chaining.
*/
EventEmitter.fn.addListener = function(evt, listener) {
// Fetch the listeners
var listeners = this.getListeners(evt);
// Push the listener into the array if it is not already there
if(this.indexOfListener(listener, listeners) === -1) {
listeners.push(listener);
}
// Return the instance of EventEmitter to allow chaining
return this;
};
/**
* Removes a listener function from the specified event.
*
* @param {String} evt Name of the event to remove the listener from.
* @param {Function} listener Method to remove from the event.
* @returns {Object} Current instance of EventEmitter for chaining.
*/
EventEmitter.fn.removeListener = function(evt, listener) {
// Fetch the listeners
// And get the index of the listener in the array
var listeners = this.getListeners(evt),
index = this.indexOfListener(listener, listeners);
// If the listener was found then remove it
if(index !== -1) {
listeners.splice(index, 1);
// If there are no more listeners in this array then remove it
if(listeners.length === 0) {
delete this._events[evt];
}
}
// Return the instance of EventEmitter to allow chaining
return this;
};
/**
* Removes all listeners from a specified event.
* If you do not specify an event then all listeners will be removed.
* That means every event will be emptied.
*
* @param {String} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
* @returns {Object} Current instance of EventEmitter for chaining.
*/
EventEmitter.fn.removeListeners = function(evt) {
// Remove different things depending on the state of evt
if(evt) {
// Remove all listeners for the specified event
delete this._events[evt];
}
else {
// Remove all listeners in all events
delete this._events;
}
// Return the instance of EventEmitter to allow chaining
return this;
};
/**
* Emits an event of your choice.
* When emitted, every listener attached to that event will be executed.
* If you pass the optional argument array then those arguments will be passed to every listener upon execution.
* Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
* So they will not arrive within the array on the other side, they will be separate.
*
* @param {String} evt Name of the event to emit and execute listeners for.
* @param {Array} [args] Optional array of arguments to be passed to each argument.
* @returns {Object} Current instance of EventEmitter for chaining.
*/
EventEmitter.fn.emitEvent = function(evt, args) {
// Get the listeners for the event
// Also initialise any other required variables
var listeners = this.getListeners(evt),
i = listeners.length;
// Make args default to an empty array
args = args || [];
while(i--) {
// Execute every listener attached to the event
// Apply the arguments array to each listener too
listeners[i].apply(null, args);
}
// Return the instance of EventEmitter to allow chaining
return this;
};
// Expose the class either via AMD or the global object
if(typeof define === 'function' && define.amd) {
define(function() {
return EventEmitter;
});
}
else {
exports.EventEmitter4 = EventEmitter;
}
}(this));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment