Reputation: 13050
I'm trying to figure out the best approach to multiple levels of inheritance when using the Node.js EventEmitter
class.
The setup is I am developing an MVC framework for Node.js using Controllers that can have base Controllers, but Controllers can emit events that also emit the same event on its base Controller. So far I have:
var events = require('events');
var util = require('util');
var Controller = function() { };
util.inherits(Controller, events.EventEmitter);
var baseController = new Controller();
baseController.on('request', function() {
console.log('Calling request event on baseController');
});
var homeController = new Controller();
homeController.prototype = baseController;
homeController.on('request', function() {
console.log('Calling request event on homeController');
});
homeController.emit('request');
...but only "Calling request event on homeController" is output, I want both 'request' events to fire. How do I do this? Maybe I need to completely change my approach?
Update: thanks for everyone's help. As Naor Biton suggested, there was no inheritance between baseController and homeController because the 'prototype' property only matters for constructor functions. I'm always getting my constructor functions and object instances mixed up.
I eventually settled for the following:
var events = require('events');
var util = require('util');
var Controller = function() { };
util.inherits(Controller, events.EventEmitter);
Controller.createBaseController = function() {
var BaseController = function() { };
util.inherits(BaseController, Controller);
return new BaseController();
};
Controller.inheritController = function(baseController) {
var NewController = function() { };
NewController.prototype = baseController;
return new NewController();
};
var baseController = Controller.createBaseController();
baseController.on('request', function() {
console.log('Calling request event on baseController');
});
var homeController = Controller.inheritController(baseController);
homeController.on('request', function() {
console.log('Calling request event on homeController');
});
homeController.emit('request');
Upvotes: 4
Views: 2375
Reputation: 276286
In your example both homeController
and baseController
have the same prototype (which you can verify using getPrototypeOf
. .prototype
is an attributes of functions.
If you want to set the prototype of an object to another object directly without using a function and setting its prototype you can use Object.create
Here is what I'd do in your case by the way (using less prototypical inheritance and more structural subtyping).
function Controller(){
var obj = new events.EventEmitter();
// Add stuff to controller here
obj.on("request",function(){
console.log('Calling request event on baseController');
});
return obj;
}
function HomeController(){
var obj = new Controller();
obj.on('request', function() {
console.log('Calling request event on homeController');
});
return obj;
}
var homeController = new HomeController(); // can create a Controller and add
// an event just fine. If you want explicit
// structural subtyping we need HomeController
homeController.emit("request");
Upvotes: 2
Reputation: 952
In your example, there is no kind of inheritance relationship between homeController and baseController. Also, this line:
homeController.prototype = baseController;
Is redundant, since the prototype property only matters for constructor functions.
If you want to set up baseController so whoever extends/ inherits from it will have the 'request' listener assigned to it, first you should define it like this:
var BaseController = function(){
this.on('request', function(){
console.log('...');
});
};
BaseController.prototype = new events.EventEmitter();
BaseController.prototype.constructor = BaseController;
It will be even better if you can make the handler assignment in an initiliazation method (doing work in a constructor function is usually bad design):
var BaseController = function(){};
BaseController.prototype = new events.EventEmitter();
BaseController.prototype.init = function(){
this.on('request', function(){
console.log('...');
});
};
And then, all you need to extend it in your HomeController is to do this:
var HomeController = function(){};
HomeController.prototype = new BaseController();
var homeController = new HomeController();
homeController.init(); // If you went with the initialization approach I offered...
Please note, that unless you must do it, creating long inheritance chains in JS isn't very nice or maintainable. If you can, I encourage you to avoid doing it and try finding a more maintainable solution.
Upvotes: 3