Question

Let's say I have two classes, Base and Child. Base is the base class that Child will be inheriting from. Here's the code to visualize this:

Base.js

function Base(init) {

}

function log() {
  console.log('here');
}
Base.prototype.log = log;

module.exports = Base;

Child.js

var Base = require('../Base.js');

var Child = module.exports = function Child(init) {
  Base.call(this, init);
};

require('util').inherits(Child, Base);

function test() {
  this.log();  // doesn't work
  Base.prototype.log();  // Works but is quite ugly
  Child.super_.prototype.log();  // Works but is even uglier
}

Child.prototype.test = test;

What I would absolutely love to do is something like this.log() or even log() would be nice. I realize I can set a variable to that in my inherited class, but then I would have to do that for every class that inherits Base, which is definitely not ideal. So my question is, can I do something like this.log() without having to set a variable in the inherited class? Am I misunderstanding something?

Was it helpful?

Solution 3

Just as an FYI, I ended up putting this in the global variable that Node creates for you. I realize that's bad practice, but it's a logging mechanism that needs to be used by any class, controller, etc., so I don't think it's that terrible of a solution.

However, in Compound, you can create no_eval controllers, which means they look like typical prototypical functions... so you can essentially create a mixin, or I can require my mixin and use it like a class... like this:

var ControllerMixin = require(process.cwd() + 'app/mixins/ControllerMixin.js');
var Log;

var LoggerController = module.exports = function LoggerController(init) {
  ControllerMixin.call(this, init);  // mixin approach
  Log = require(process.cwd() + 'app/utils/LoggerMixin.js')(init);  // class approach
};

LoggerController.prototype.index = function index(controller) {
  controller.logMessage('blah');  // using mixin
  Log.logError('hi');  // using class
  global.logWarning('yep');  // global approach
  return controller.send({success: true});
};

So there are options... just have to find what you think is the best approach.

OTHER TIPS

Updated Answer:

From your comment below, replying to my statment that this.log() should work:

Well, so that's the thing. When I'm in Child's test function, this is an empty object, so I'm assuming somewhere down the line I'm not getting the proper scope.

You haven't shown how you're calling test, but I suspect that's where the problem is. Provided you call it via a Child instance:

var c = new Child();
c.test();

...then within the call, this will be the child instance, which will inherit (indirectly) the Parent.prototype object with its log property.

But how you call it is important. This wouldn't work, for instance:

var c = new Child();
var f = c.test;
f();

If you do that, within the call to the function, this will be the global object (or undefined if you're in strict mode), not a Child instance. This is because in JavaScript, this is set primarily by how a function is called, and calling it like that doesn't set this to what you want.

This matters for callbacks, because passing in c.test as a callback:

someFunctionThatUsesACallback(c.test);

...means the code calling back won't set this for you.

If you need to do that, Function#bind will help:

var f = c.test.bind(c); // Returns a version of c.test that, when called,
                        // will have `this` set to `c`
f();                    // Works, `this` is the `Child` instance

And similarly:

someFunctionThatUsesACallback(c.test.bind(c));

More (on my blog):


Original Answer:

If you set up the prototype hierarchy correctly, and Child.prototype doesn't have log on it (and you don't put a log property on instances), then you should be able to use this.log(); just fine. If you can't, then the hierarchy hasn't been set up correctly.

I don't know what util.inherits does, but setting up the relationship between Child and Parent correctly isn't complicated:

function Parent() {
}
Parent.prototype.log = function() {
    console.log("log called");
};

function Child () {
    Parent.call(this);
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child; // This line is largely optional, but a good idea

// Usage
var c = new Child();
c.log(); // "log called"

But if you override log in your Child.prototype or assign a log property to instances, and you want to use Parent's version of log, then of course you can't just use this.log() because the property doesn't refer to Parent.prototype.log anymore.

When you need to call the parent version of something (I call them "supercalls," and I don't think that's original), you have to do more work:

I usually set up hierarchies like this by passing the parent constructor into a function I use to build the child, e.g.:

var Child = (function(Super) {
    var pp = Super.prototype;

    function Child() {
    }
    Child.prototype = Object.create(pp);

    Child.prototype.doSomething = function() {
        // Call `log` with appropriate `this`
        pp.log.call(this);
    };

    return Child;
})(Parent);

By always using that pattern, I avoid having to write Parent inside the Child code (I use the Super arg instead), so if I need to rebase Child, I just change what I pass into the function.

Because that's fairly ugly (for instance, it's unclear at the top of Child that it derives from Parent, since Parent is at the bottom) and involves boilerplate code I don't feel the need to write again every time, I wrote a simple helper script for it I call Lineage, which makes it look like this:

var Child = Lineage.define(Parent, function(p, pp) {
    p.doSomething = function() {
        // Call `log` with appropriate `this`
        pp.log.call(this);
    };
});

Note that Lineage passes in both the Child and Parent prototypes as arguments, making it concise to use them (and since you get to pick those argumetn names, you can use whatever terminology works for you — I use p for the prototype of the "class" being created [Child in the above], and pp for the parent's prototype, etc.).

The standard inherits function from node.js is (in my humble opinion) very bad code. Instead I prefer to use augment to create classes:

// base.js

var augment = require("augment");

var Base = module.exports = augment(Object, function () {
    this.constructor = function (init) {

    };

    this.log = function () {
        console.log("here");
    };
});

// child.js

var augment = require("augment");
var Base = require("./base");

var Child = module.exports = augment(Base, function (base) {
    this.constructor = function (init) {
        base.constructor.call(this, init);
    };

    this.test = function () {
        this.log();
    };
});

In addition augment.js is just 20 lines of code and can be used everywhere.

Every answer I see online either looks complicated, or relies on external libraries. Why not just boil down to the basics, assuming you use a custom type design pattern, which is very similar to traditional OOP.

parent.js

//the main parent class
// class Parent


module.exports = Parent;

function Parent(a) {
    if (!(this instanceof Parent)) {
        return new Parent(a);
    }
    this.a = a; //Some parent variable
}

Parent.prototype = {

    // Instantiate child:
    getChild: function() {
        var Child = require('./child');
        return new Child(this);
    },

    // Some parent function, print some text:
    printText: function(text) {
        console.log(text);
    }
};

child.js

//Similar to class Child extends Parent


module.exports = Child;

function Child(parent) {

    this.parent = parent;
}

Child.prototype = {

    // Prints Parent Variable:
    printParentVariable: function() {
        console.log(this.parent.a);
    },

    // Calls Parent Function:
    callParentFunction: function() {
        this.parent.printText('Child calling parent function.');
    }
};

test.js

var parent = require('./parent')('parent text'); //instantiate parent with some text

var child = parent.getChild();       //create instance of a child

//*** Child has full access to its parents variables and methods ***//

console.log(child.parent.a);         //Print the parent text "parent text"
child.printParentVariable();         //Child method which prints the parent variable "parent text", identical to line above.
child.parent.printText('Child calling parent');      //Call parent method, to print provided text
child.callParentFunction();          //Calls parent method, identical to above.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top