Sunday Ironfoot
Sunday Ironfoot

Reputation: 13050

Node.js EventEmitter and multiple levels of inheritance

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

Answers (2)

Benjamin Gruenbaum
Benjamin Gruenbaum

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

Naor Biton
Naor Biton

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

Related Questions