Reputation: 13810
I see this pattern in quite a few Node.js libraries:
Master.prototype.__proto__ = EventEmitter.prototype;
(source here)
Can someone please explain to me with an example, why this is such a common pattern and when it's handy?
Upvotes: 98
Views: 101593
Reputation: 63653
As the comment above that code says, it will make Master
inherit from EventEmitter.prototype
, so you can use instances of that 'class' to emit and listen to events.
For example you could now do:
masterInstance = new Master();
masterInstance.on('an_event', function () {
console.log('an event has happened');
});
// trigger the event
masterInstance.emit('an_event');
Update: as many users pointed out, the 'standard' way of doing that in Node would be to use 'util.inherits':
var EventEmitter = require('events').EventEmitter;
util.inherits(Master, EventEmitter);
2nd Update: with ES6 classes upon us, it is recommended to extend the EventEmitter
class now:
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
myEmitter.emit('event');
See https://nodejs.org/api/events.html#events_events
Upvotes: 96
Reputation: 14226
The Node docs now recommend using class inheritence to make your own event emitter:
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {
// Add any custom methods here
}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
myEmitter.emit('event');
Note: If you define a constructor()
function in MyEmitter
, you should call super()
from it to ensure the parent class's constructor is called too, unless you have a good reason not to.
Upvotes: 92
Reputation: 228
To add to wprl's response. He missed the "prototype" part:
function EventedObject(){
// Super constructor
EventEmitter.call(this);
return this;
}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part
Upvotes: 2
Reputation: 44205
This is how prototypical (prototypal?) inheritance is done in JavaScript. From MDN:
Refers to the prototype of the object, which may be an object or null (which usually means the object is Object.prototype, which has no prototype). It is sometimes used to implement prototype-inheritance based property lookup.
This works as well:
var Emitter = function(obj) {
this.obj = obj;
}
// DON'T Emitter.prototype = new require('events').EventEmitter();
Emitter.prototype = Object.create(require('events').EventEmitter.prototype);
Understanding JavaScript OOP is one of the best articles I read lately on OOP in ECMAScript 5.
Upvotes: 19
Reputation: 14419
To inherit from another Javascript object, Node.js's EventEmitter in particular but really any object in general, you need to do two things:
[[proto]]
for objects created from your constructor; in the case that you're inheriting from some other object, you probably want to use an instance of the other object as your prototype.This is more complicated in Javascript than it might seem in other languages because
For the specific case of Node.js's EventEmitter, here's what works:
var EventEmitter = require('events').EventEmitter;
var util = require('util');
// Define the constructor for your derived "class"
function Master(arg1, arg2) {
// call the super constructor to initialize `this`
EventEmitter.call(this);
// your own initialization of `this` follows here
};
// Declare that your class should use EventEmitter as its prototype.
// This is roughly equivalent to: Master.prototype = Object.create(EventEmitter.prototype)
util.inherits(Master, EventEmitter);
Possible foibles:
util.inherits
, but don't call the super constructor (EventEmitter
) for instances of your class, they won't be properly initialized.new EventEmitter
) as Master.prototype
instead of having the subclass constructor Master
call the super constructor EventEmitter
; depending on the behavior of the superclass constructor that might seem like it's working fine for a while, but is not the same thing (and won't work for EventEmitter).Master.prototype = EventEmitter.prototype
) instead of adding an additional layer of object via Object.create; this might seem like it's working fine until someone monkeypatches your object Master
and has inadvertently also monkeypatched EventEmitter
and all its other descendants. Each "class" should have its own prototype.Again: to inherit from EventEmitter (or really any existing object "class"), you want to define a constructor that chains to the super constructor and provides a prototype that is derived from the super prototype.
Upvotes: 41
Reputation: 25387
I thought this approach from http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm was pretty neat:
function EventedObject(){
// Super constructor
EventEmitter.call( this );
return( this );
}
Douglas Crockford has some interesting inheritence patterns too: http://www.crockford.com/javascript/inheritance.html
I find inheritence is less often needed in JavaScript and Node.js. But in writing an app where inheritence might affect scalability, I would consider performance weighed against maintainability. Otherwise, I would only base my decision on which patterns lead to better overall designs, are more maintainable, and less error-prone.
Test different patterns out in jsPerf, using Google Chrome (V8) to get a rough comparison. V8 is the JavaScript engine used by both Node.js and Chrome.
Here're some jsPerfs to get you started:
http://jsperf.com/prototypes-vs-functions/4
http://jsperf.com/inheritance-proto-vs-object-create
http://jsperf.com/inheritance-perf
Upvotes: 4