96

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?

jeffreyveon
  • 13,400
  • 18
  • 79
  • 129
  • Refer to this question for info http://stackoverflow.com/questions/5398487/confusion-about-setting-something-prototype-proto – Juicy Scripter Jan 17 '12 at 16:55
  • 14
    Note `__proto__` is an anti-pattern, please use `Master.prototype = Object.create(EventEmitter.prototype);` – Raynos Jan 17 '12 at 17:36
  • 70
    Actually, use ```util.inherits(Master, EventEmitter);``` – thesmart Nov 09 '12 at 10:00
  • 1
    @Raynos What's an anti-pattern? – starbeamrainbowlabs Aug 01 '15 at 17:07
  • 1
    This is now easier with ES6 Class constructors. Check compat here: https://kangax.github.io/compat-table/es6/ . Check the docs or my answer below. – Breedly Mar 11 '16 at 16:26
  • @Breedly is right. Use of `util.inherits` has been discouraged by the docs and the recommended way is using ES6 classes. `util.inherits` has flaws: https://github.com/nodejs/node/issues/4179 – Andre Pena Jun 09 '19 at 22:23

6 Answers6

94

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

alessioalex
  • 62,577
  • 16
  • 155
  • 122
  • 5
    (just a little reminder to do `require('events').EventEmitter` first-- I always forget. Here's the link to the docs in case anyone else needs it: http://nodejs.org/api/events.html#events_class_events_eventemitter) – mikermcneil Feb 14 '14 at 23:24
  • 3
    BTW, the convention for instances is to lowercase the first letter, so `MasterInstance` should be `masterInstance`. – Chris Apr 27 '14 at 21:49
  • And how do you retain ability to check if: masterInstance instanceof Master? – jayarjo May 17 '14 at 11:00
  • @jayarjo it works by default, you don't have to do anything special – alessioalex May 17 '14 at 15:12
  • If you export constructor from the module, like: "module.exports = Master;" and then create an instance of it, then "masterInstance instanceof Master" will fail (not sure if it's expected?) – jayarjo May 19 '14 at 08:01
  • Nope it won't, maybe you're just doing something wrong. – alessioalex May 19 '14 at 09:04
  • 3
    `util.inherits` does a nasty thing of injecting the `super_` property into the `Master` object. It's unnecessary and tries to treat prototypical inheritance like classic inheritance. Take a look at the bottom of [this](http://www.crockford.com/javascript/inheritance.html) page for explanation. – klh Mar 02 '15 at 14:53
  • @Killah so, that said, which is the alternative? – loretoparisi Apr 20 '17 at 17:53
  • 2
    @loretoparisi just `Master.prototype = EventEmitter.prototype;`. No need for supers. You can also use ES6 extends (and it's encouraged in Node.js docs on `util.inherits`) like this `class Master extends EventEmitter` - you get classical `super()`, but without injecting anything into `Master`. – klh Apr 21 '17 at 21:12
  • As alluded to below, this is discouraged now according to [this](https://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor) – KardJaster Jan 13 '20 at 19:59
  • Since `MyEmitter` in `class MyEmitter extends EventEmitter {}` is not adding anything to `EventEmitter` - couldn't you just `const myEmitter = new EventEmitter()` instead? – justin Apr 15 '20 at 13:47
89

ES6 Style Class Inheritance

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.

callum
  • 34,206
  • 35
  • 106
  • 163
Breedly
  • 12,838
  • 13
  • 59
  • 83
  • 10
    These comments are not correct and end up being misleading in this case. Calling `super()` is **not required**, as long as you don't need/define a constructor, hence Breedly's original answer (see EDIT history) was entirely correct. In this case you can copy and paste this very same example in the repl, remove the constructor entirely and it will work the same way. That is perfectly valid syntax. – Aurelio Jul 11 '17 at 07:26
41

To inherit from another Javascript object, Node.js's EventEmitter in particular but really any object in general, you need to do two things:

  • provide a constructor for your object, which completely initializes the object; in the case that you're inheriting from some other object, you probably want to delegate some of this initialization work to the super constructor.
  • provide a prototype object that will be used as the [[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

  • Javascript separates object behavior into "constructor" and "prototype". These concepts are meant to be used together, but can be used separately.
  • Javascript is a very malleable language and people use it differently and there is no single true definition of what "inheritance" means.
  • In many cases, you can get away with doing a subset of what's correct, and you'll find tons of examples to follow (including some other answers to this SO question) that seem to work fine for your case.

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:

  • If you use set the prototype for your subclass (Master.prototype), with or without using util.inherits, but don't call the super constructor (EventEmitter) for instances of your class, they won't be properly initialized.
  • If you call the super constructor but don't set the prototype, EventEmitter methods won't work on your object
  • You might try to use an initialized instance of the superclass (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).
  • You might try to use the super prototype directly (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.

metamatt
  • 13,809
  • 7
  • 46
  • 56
19

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.

Joshua Ball
  • 23,260
  • 6
  • 27
  • 29
Daff
  • 43,734
  • 9
  • 106
  • 120
  • 7
    `Y.prototype = new X();` is an anti-pattern, please use `Y.prototype = Object.create(X.prototype);` – Raynos Jan 17 '12 at 17:35
  • This is good to know. Can I read up more somewhere? Would be interested how the resulting objects are different. – Daff Jan 17 '12 at 17:44
  • 4
    `new X()` instantiate an instance of `X.prototype` and initializes it by invoke `X` on it. `Object.create(X.prototype)` just instantiates an instance. You don't wnat `Emitter.prototype` to be initialized. I can't find a good article that explains this. – Raynos Jan 17 '12 at 17:49
  • This makes sense. Thanks for pointing it out. Still trying to get into good habits on Node. The browsers are just not there for ECMA5 (and as I understood the shim is not the most reliable). – Daff Jan 17 '12 at 19:00
  • The shim is 99% reliable. Browsers are there for ECMA5. The only major ECMA5 feature that doesnt work in IE<9 is getter/setters. The rest is shimmable or you dont care about it – Raynos Jan 17 '12 at 19:01
  • Cool, I think I will give it a more thorough try then. – Daff Jan 17 '12 at 19:35
  • When i do inheritance like how u said, i loose all existing properties i had on derived class. I also loose all properties of the base class which were not on the prototype of the base class. This i what i am doing: Derived.prototype = Object.create(Base.prototype). To solve the issue I am creating a json of properies I am loosing and passing them in as the second argument to Object.create. Is that correct? – user566245 Jun 19 '12 at 16:55
  • You will use var Derived = Object.create(Base). Check out the inheritance library I created: http://daffl.github.com/uberproto/ – Daff Jun 20 '12 at 01:11
  • 1
    The other link is also broken. Try this one: http://robotlolita.github.io/2011/10/09/understanding-javascript-oop.html – jlukanta Feb 19 '14 at 17:17
5

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

wprl
  • 24,489
  • 11
  • 55
  • 70
1

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
guatedude2
  • 218
  • 3
  • 5
  • 2
    Actually, you should use Object.create instead of new otherwise you get instance state as well as behaviour on the prototype as explained [elsewhere](http://stackoverflow.com/a/16063711/2670182). But better to use ES6 and transpile or `util.inherits` since a lot of smart people will keep those options up to date for you. – Cool Blue Aug 10 '16 at 03:28