Jack Slingerland
Jack Slingerland

Reputation: 2811

Issue with prototype inheritance in Javascript (Node.js)

I have two files: BaseController.js and EventRecordController.js. EventRecord needs to inherit a few methods from BaseController.

BaseController

var Q = require('q'),
    util = require('../util');

exports.BaseController = function(req, res) {
    this.req = res;
    this.res = res;
    this.fields = {};
    this.error = {
        code: 200,
        message: 'BAD REQUEST: The parameters provided were invalid. See response body for error messages.',
        specific_message: ''
    };
};

// Utility method to handle returning errors that are thrown.
exports.BaseController.prototype.handle_errors = function(error) {
    if(this.error.code === 500) {
        util.internal_error(this.res, this.response_type);
    } else {
        var response = util.build_error_response(this.response_type, this.error.code, this.error.message, this.error.specific_message);
        util.send_response(this.res, this.response_type, this.error.code, response);
    }
};

// Check to see if a user is authenticated and whether they are using a correct response type.
exports.BaseController.prototype.validate_response_type_and_authenticate = function() {
    var deferred = Q.defer();
    util.validate_response_type_and_authenticate(this.req, this.res, function(auth_data, response_type) {

        this.auth_data = auth_data;
        this.company_user_uid = this.auth_data.data.company.uid;
        this.response_type = response_type;
        this.v3_token = this.auth_data.data.token;

        deferred.resolve();
    });
    return deferred.promise;
};

EventRecordController

var base_controller = require("./BaseController"),
    Q = require('q'),
    util = require('../util'),
    validator = require('validator');


exports.EventRecordController = function(req, res) {

    function EventRecord(req, res) {
        base_controller.BaseController.apply(this, arguments);
    }

    // Inherit from BaseController, then fix constructor.
    EventRecord.prototype = new base_controller.BaseController();
    EventRecord.prototype.constructor = EventRecord;

    EventRecord.run = function() {
        console.log(this.error);
    };

    return EventRecord;
};

When I run the following code, this.error logs as undefined from within the run() method.

var event_record_controller = require("./controllers/EventRecordController"),
    util = require('./util'),
    validator = require('validator');

exports.record = function(req, res) {
    var controller = new event_record_controller.EventRecordController(req, res);
    controller.run();
};

I think I'm missing something obvious here, but my experience with prototype based inheritance is limited.

Upvotes: 0

Views: 216

Answers (1)

Jonathan Lonowski
Jonathan Lonowski

Reputation: 123573

this.error is undefined because run is being called directly on the constructor, which doesn't have an error, rather than one of its instances.

Methods that are attached directly to the constructor aren't inherited. For that, they should be attached to the prototype:

// "static" method available only through the constructor itself
EventRecord.run = function() {
    console.log(this.error);
};

// "class" method inherited by instances of `EventRecord`
EventRecord.prototype.run = function () {
    console.log(this.error);
};

But, you also don't yet have an instance of EventRecord to call .run() on.

When a constructor returns an object, the instance that was created by using new will be discarded. So, calling new EventRecordController() is just returning the function EventRecord.

var controller = new event_record_controller.EventRecordController(req, res);
console.log(typeof controller);     // function
console.log(controller.name);       // "EventRecord"

controller = new controller(req, res);
console.log(typeof controller);     // object

You could revise EventRecordController to return an instance of EventRecord:

// ...

return new EventRecord(req, res);

Though, you might consider consolidating the 2 constructors rather than having one generate the other:

exports.EventRecordController = function(req, res) {
    base_controller.BaseController.apply(this, arguments);
};

util.inherits(exports.EventRecordController, base_controller.BaseController);

exports.EventRecordController.prototype.run = function () {
    console.log(this.error);
};

Upvotes: 1

Related Questions