Reputation: 418
I am learning Node Js. I have encountered a snippet of code in my book that stated as follow:
var EventEmitter = require("events").EventEmitter;
var inherits = require('util').inherits;
//Custom class
function Foo(){
EventEmitter.call(this);
}
inherits(Foo, EventEmitter);
Foo.prototype.connect = function(){
this.emit('connected');
}
var foo = new Foo();
foo.on('connected', function(){
console.log("connected raised!');
}
foo.connect();
My question is that what is "call" here do? Why does the class Foo inherits from EventEmitter? Does that mean that Foo is a child of Event Emitter? If so whu must it be a child of the EventEmitter? I have found another question in Stackoverflow regarding the call (What does EventEmitter.call() do?) However, I did not understand the answer provided... Thanks
Source of the code: Beginning Node.js by Basarat Ali Syed
Upvotes: 4
Views: 2161
Reputation: 709
When applying:
inherits(Foo, EventEmitter);
Foo
's prototype is set to EventEmitter.prototype
. So, each instance of Foo
will have all the of EventEmitter
's methods (e.g on
, emit
etc.)
When Applying:
EventEmitter.call(this)
within Foo
's constructor, it is very similar to calling new EventEmitter()
, but instead of creating a new context (variable this
), you are passing your Foo
's context.
For example, here is the EventEmitter
's constructor source:
function EventEmitter() {
this._events = new Events();
this._eventsCount = 0;
}
The above class members (this._events
& this._eventCount
) are necessary for maintaining the private state of the event emitter.
Applying merely inherits(Foo, EventEmitter)
will enhance Foo
's instances with EventEmitter
's methods, however Foo
's instances will lack a very basic and crucial initialisation process.
In case a super class has an empty constructor you can skip this step, since it has nothing to assign to this
variable. Having said that, this is a bad practice since you have no guarantee over this.
Upvotes: 2
Reputation: 59244
It's the ghetto JS way to do inheritance since the language only really supports prototypal inheritance. There is no official language supported class inheritance like in other languages, but the fact that we can run functions against different contexts has become a pretty standard hack for shoehorning in an inheritance hierarchy where an instance of your class can also be considered an instance of its base class. In other words, in var foo = new Foo()
, foo
can be said to be both an instance of Foo
and an EventEmitter
. In other languages you can setup more explicit inheritance that is supported by the language and compiler.
.call
is available on all functions and lets you execute the function, but with a different context. this
in your Foo
class refers to the instance of Foo
being created and EventEmitter.call(this);
is running the EventEmitter
constructor, but using your instance of Foo
as the this
inside the EventEmitter
constructor. This way, anything that the EventEmitter
constructor would normally setup on a new instance of pure EventEmitter
via var emitter = new EventEmitter();
, will now actually be setup on your instance of Foo
.
Now, this only solves half the goal of achieving JS pseudo-inheritance. If there is anything on the EventEmitter
prototype that we need to be on our Foo
prototype, merely calling the EventEmitter
constructor on your instance of Foo
is not enough. This is why you also have to call util.inherits(Foo, EventEmitter);
.
util.inherits
just sets the prototype of Foo
to a new object that inherits from the prototype of EventEmitter
. It also adds the EventEmitter
constructor as a .super_
property to your Foo
constructor which I believe is a Java convention to make it easy to access the base constructor (the constructor of the class you inherit from). https://github.com/joyent/node/blob/master/lib/util.js#L634-L644
Upvotes: -2
Reputation: 708156
The line of code:
EventEmitter.call(this);
calls the constructor of the object you are inheriting from which allows the EventEmitter code to initialize its portion of this object which is part of the inheritance process in Javascript.
EventEmitter()
is the constructor function for the EventEmitter object. Since you need to call that constructor with the same this
as your new object, you must use either .call()
or .apply()
with that constructor in order to cause the correct this
to be used. Since there are no arguments you are passing to the constructor, .call()
is the simplest way to call it.
You must call the EventEmitter()
constructor in order to allow it to properly initialize its portion of the object that was created with new Foo()
. When using inheritance in Javascript, multiple separate object definitions are using the same object to store their properties and methods so each much initialize their portion of the object and that initialization is started by calling the constructor of the object that you inherited from.
Here's a good reference on the topic of chaining constructors.
It appears from some of your comments that you don't understand what the point of the inheritance in your code is. That code allows you to create an object type Foo
that has your own methods on it, but that object ALSO is an eventEmitter and has all the capabilities of an EventEmitter such that it can trigger events, respond to events, etc... This is called "inheritance" where you inherit the functionality of some other object with your own custom object. To make inheritance work, your code does two things. With the inherits(Foo, EventEmitter);
line of code, it inherits the prototype of the other object so that it will have all the same methods available and with EventEmitter.call(this);
, it calls the constructor of the inherited object so that object can initialize itself properly.
You may want to read a couple of reference articles on Javascript inheritance:
Introduction to Object-Oriented JavaScript
Inheritance and the prototype chain
Understanding JavaScript Inheritance
What is "inheritance" in Javascript?
Inheritance: Object Oriented Programming
Upvotes: 6