Sriram
Sriram

Reputation: 787

Recursive prototype inheritance in nodejs / javascript

In my nodejs program, i have a module called person and it has a prototype object(about) which also has its child method (describe). I am trying to access objects properties but i am getting undefined.

Could someone correct my understanding? what i am doing wrong here?

index.js

var Person = require("./person");
var sam = new Person({name:"Sam",age:23})
sam.about.describe();

person.js

module.exports = (function() {
    var person = function(options) {
        if (options && options.name) this.name = options.name;
        if (options && options.age) this.age = options.age;
    }
    person.prototype.about = {
        describe : function(){
            console.log("I am",this.name,"and",this.age,"years old");
        }
    }
    return person;
})();

Expected output: "I am Sam and 23 years old" Actual output: "I am undefined and undefined years old"

Upvotes: 3

Views: 195

Answers (3)

user663031
user663031

Reputation:

If you really want to be able to use sam.about.describe, with an about "namespace" containing methods, then one approach is

Object.defineProperty(person.prototype, 'about', {
  get() { 
    return { 
      describe: () => console.log("I am", this.name, "and", this.age, "years old"); 
    };
  }
})

This works because the context within get is that of the Person instance, and the arrow function will thus refer to it properly.

Upvotes: 0

Sebas
Sebas

Reputation: 21532

It's because this refers to the direct parent of describe, which is about:

person.prototype.about = {
        describe : function() {
            console.log("I am",this.name,"and",this.age,"years old");
        }
}

You'd need a weak reference to the main Person object passed to the about namespace for example, and use it in place of this. But I don't like it that way, it is circumvoluted, feels wrong and looks smelly.

Instead, let's just aknowledge the fact that it's a completely new functionality package added to the Person object and let's make a helper for it:

module.exports = (function() {
    var PersonHelper = function(person) {
        this.person = person;
        describePerson: function() {
            console.log("I am",this.person.name,"and",this.person.age,"years old");
        }
        /* more methods */
    }
    return PersonHelper;
})();

So you could then do:

module.exports = (function() {
    var person = function(options) {
        if (options && options.name) this.name = options.name;
        if (options && options.age) this.age = options.age;
    }
    return person;
})();


var Person = require("./person");
var PersonHelper = require("./personHelper");
var sam = new Person({name:"Sam",age:23})
var helper = new PersonHelper(person);
helper.describePerson();

Upvotes: 1

Martin Adámek
Martin Adámek

Reputation: 18389

As others have said, this in your example refers to the about object, not the person instance itself.

One way to have the API you want, is to create that about namespace within the constructor, and use bind to set correct context to the describe handler.

module.exports = (function() {
    var person = function(options) {
        if (options && options.name) this.name = options.name;
        if (options && options.age) this.age = options.age;
        this.about = {
            describe: function () {
                console.log("I am",this.name,"and",this.age,"years old");
            }.bind(this)
        };
    }

    return person;
})();

This way you can simply call

var Person = require("./person");
var sam = new Person({name:"Sam",age:23})
sam.about.describe();

>>> I am Sam and 23 years old

Upvotes: 1

Related Questions