9

I was reading over this small article to understand inheriting from EventEmitter, but I'm a little confused.

He does this:

function Door() {
    events.EventEmitter.call(this);
    this.open = function() {
        this.emit('open');
    };
}
Door.prototype.__proto__ = events.EventEmitter.prototype;

https://gist.github.com/chevex/7646362

Why does he manually invoke the EventEmitter constructor with his own constructor's this? Also, why does he set the prototype of his contsructor's prototype to the prototype of EventEmitter? That's super confusing to me.

Then someone in the comments suggested he do this instead, which seemed more elegant:

function Door() {
    events.EventEmitter.call(this);
    this.open = function () {
      this.emit('open');
    }
}
util.inherits(Door, events.EventEmitter);

https://gist.github.com/chevex/7646447

This seems WAY cleaner than the other way, though that's probably just because I fail to understand what's going on in the first instance. I would not be surprised if util.inherits is doing the same thing as the first example.

The second one at least makes a little sense to me, but I still don't understand why they don't just do this:

function Door() {
    this.open = function () {
      this.emit('open');
    }
}
Door.prototype = new events.EventEmitter();

https://gist.github.com/chevex/7646524

Can anyone explain to me what the differences between all of these approaches is and why in the first two they invoke .call(this) on the EventEmitter constructor? I omitted that line while trying out the examples and they still worked.

CatDadCode
  • 58,507
  • 61
  • 212
  • 318
  • AFAIK, `util.inherits` actually does the same asin your first example. It's the only way to implement inheritance in JavaScript – Darkhogg Nov 25 '13 at 19:19
  • You mean the only way other than my third example? – CatDadCode Nov 25 '13 at 19:23
  • I really don't know how correct is the third way, but it just looks like cheating... Also, as pointed out in an answer, `inherits` does not really dowhat I thought. – Darkhogg Nov 25 '13 at 19:26
  • I'm not sure how it's cheating considering it's the exact way I saw it done in [this book](http://www.amazon.com/gp/product/0596805527/ref=oh_details_o00_s00_i00?ie=UTF8&psc=1) when demonstrating the basics of inheritance. Though it's not impossible that the book was wrong. – CatDadCode Nov 25 '13 at 19:33
  • 1
    I *looks* like. Everyone always tell that prototypical inheritanceis hard. That way, it doesn't look hard! I'm actually confused. – Darkhogg Nov 25 '13 at 19:33
  • I've actually never found prototype inheritance to be that difficult......until now lol. It's always seemed fairly straightforward to me. – CatDadCode Nov 25 '13 at 19:44
  • Don't forget to add the "constructor" property and "instanceof" to you list of things to study for another level of gotchas to master. – Peter Lyons Nov 25 '13 at 20:31

4 Answers4

9

The third example is not generally correct: that creates one single EventEmitter instance for all door instances.

Let's imagine a simple case:

var Foo = function() {
    // each Foo instance has a unique id
    this.id = Math.random();
}
Foo.prototype.doFoo = function() { console.log("Foo!"); }

Suppose we want to create a Bar constructor that inherits from Foo and adds some new properties. If you follow your final example:

var Bar = function() {
    this.something = 5;
}
Bar.prototype = new Foo();

This is wrong because all Bar instance will have the same id property. Instead, we must call the parent constructor for each instance:

var Bar = function() {
    Foo.call(this);  // set unique `id` on `this`
    this.something = 5;
}
Bar.prototype = Object.create(Foo.prototype);

Note that the final line here is the same as Bar.prototype.__proto__ = Foo.prototype; because Object.create creates a new object whose __proto__ is set to the Object.create argument.

apsillers
  • 112,806
  • 17
  • 235
  • 239
  • This is exactly the explanation I was looking for. Thanks so much. This way you can add things to your object's prototype without modifying the prototype of `EventEmitter`. And we do `EventEmitter.call(this)` in case there is any code in the constructor that needs to be applied and wasn't already part of the prototype we just attached. Got it! – CatDadCode Nov 25 '13 at 19:41
3

Why does he manually invoke the EventEmitter constructor with his own constructor's this?

This is necessary to make sure whatever code is in the EventEmitter constructor function is executed. Some classes might not do anything interesting in the constructor, but others will have important code there, so you should always do this to make sure that code runs the same way it would run if you had just made a new EventEmitter directly with var emitter = new EventEmitter;

In some languages (such as Java) this "constructor chaining" type behavior is implicit, but in JavaScript it must be explicitly done.

Exactly how to set up inheritance in JavaScript comes down to an "it's complicated" answer and others can probably do it more justice than I. There are also several viable variations and people differ on which is preferable. However, FYI the source for util.inherits is here and the best in-depth examination of all the flavors of doing this I have seen is in this video: Douglas Crockford: Advanced JavaScript. Basically, watch that in it's entirety periodically until it sinks in.

Places to look for reference. If you fully understand how all these works, you've mastered it (it still turns my brain into a pretzel at some point along the way)

Peter Lyons
  • 142,938
  • 30
  • 279
  • 274
  • That makes sense. However, I'm still confused why he sets the prototype of the prototype (`Door.prototype.__proto__ = events.EventEmitter.prototype`). Shouldn't it just be `Door.prototype = events.EventEmitter.prototype`? – CatDadCode Nov 25 '13 at 19:24
  • See the hour-long video I linked to for details on that part. Tricky to explain concisely in a SO answer. – Peter Lyons Nov 25 '13 at 19:26
  • 3
    @AlexFord See http://stackoverflow.com/q/10393869/710446 -- one obvious practical problem is that you can't add child-only properties. Setting `Doo.prototype.foo` would then also set `EventEmitter.prototype.foo` which isn't likely what you want. – apsillers Nov 25 '13 at 19:30
  • There is a difference between function's `prototype` property and object's `__proto__` – vkurchatkin Nov 25 '13 at 19:31
  • @apsillers Ohhhhhhhhhhh *light bulb*. Thank you. I believe that also explains why setting `Door.prototype = new EventEmitter();` is bad? Emitting an event on one instance of `Door` would trigger all listeners on all instances of `Door` yes? – CatDadCode Nov 25 '13 at 19:34
  • @vkurchatkin I do understand the difference between a constructor's `prototype` and an instance's `__proto__`. – CatDadCode Nov 25 '13 at 19:34
3
function Door() {
    events.EventEmitter.call(this);
}

Door.prototype.__proto__ = events.EventEmitter.prototype;

In this case using events.EventEmitter.call(this) is like using super in languages which have one. It's actually can be omitted in simple cases, but it will break domain support on current node version and maybe something else in future versions.

Setting __proto__ just sets up the prototype chain. At can be also done like this:

Door.prototype = Object.create(events.EventEmitter.prototype);

but in this case you also need to set constructor property manually.

util.inherits(Door, events.EventEmitter);

This is the idiomatic way of inheriting in node. So you are better to use this. But what it does is basically the same as above.

function Door() {
}
Door.prototype = new events.EventEmitter();

And this is the WRONG way, don't use it! You will end with having events shared between instances in some versions of node.

vkurchatkin
  • 13,364
  • 2
  • 47
  • 55
2

The Node v6.3.1 documentation states about util.inherits(constructor, superConstructor):

Usage of util.inherits() is discouraged. Please use the ES6 class and extends keywords to get language level inheritance support. Also note that the two styles are semantically incompatible.


The following code shows how to inherit from EventEmitter with Typescript:

import { EventEmitter } from "events"

class Person extends EventEmitter {
    constructor(public name: string) {
        super()
    }
}

let person = new Person("Bob")
person.on("speak", function (said: string) {
    console.log(`${this.name} said: ${said}`)
})
person.emit("speak", "'hello'") // prints "Bob said: 'hello'"

The previous code will transpile into the following ES6 code:

"use strict";
const events = require("events");
class Person extends events.EventEmitter {
    constructor(name) {
        super();
        this.name = name;
    }
}
let person = new Person("Bob");
person.on("speak", function (said) { console.log(`${this.name} said: ${said}`); });
person.emit("speak", "'hello'");
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218