Skip to content Skip to sidebar Skip to footer

Javascript Prototypal Inheritance - Descendants Override Each Other

I'm creating two objects (Inherits), both inherit from Base. The second object's property assignment overrides the value in the first object. Any thoughts? How to make a proper inh

Solution 1:

Problem(s)

  • The Base class constructor is called once and only once. this.prototype = new baseClass;
  • Privileged methods will refer to the same this object across instances because those methods are created within the constructor (which was called only once).

Problem(s) based on a matter of opinion

  • You should try to avoid modifying native prototypes (ie. Function.prototype) if you plan on working alongside JavaScript that you do not own.

Solution

  • Call the constructor and parent constructor(s) for each new instance that is created.
  • Inherit the parent prototype chain (not a instance of a the parent class).
  • Maintain a 1:1 ratio of number of instances created to the number of calls the constructor(s) in the prototype chain.

Final solution to Max's problem

Please pay special attention to the inherits function and the ParentClass.call(this, title); line. Constructors to look for are ParentClass and ChildClass

/**
 * Allows a child constructor to safely inherit the parent constructors prototype chain.
 * @type {Function}
 * @param {!Function} childConstructor
 * @param {!Function} parentConstructor
 */functioninherits(childConstructor, parentConstructor){
    varTempConstructor = function(){};
    TempConstructor.prototype = parentConstructor.prototype; // Inherit parent prototype chain

    childConstructor.prototype = newTempConstructor(); // Create buffer object to prevent assignments directly to parent prototype reference.
    childConstructor.prototype.constructor = childConstructor; // Reset the constructor property back the the child constructor
};

///////////////////////////////////////** @constructor */functionParentClass(title) {
    this.setTitle(title);

    var randId_ = Math.random();
    /** @return {number} */this.getPrivlegedRandId = function()
        {return randId_;};
};

/** @return {string} */ParentClass.prototype.getTitle = function()
    {returnthis.title_;};

/** @param {string} value */ParentClass.prototype.setTitle = function(value)
    {this.title_ = value;};

//////////////////////////////////////    /**
 * @constructor
 * @param {string} title
 * @param {string} name 
 */ChildClass = function (name, title) {
    ParentClass.call(this, title); // Call the parent class constructor with the required argumentsthis.setName(name);
}
inherits(ChildClass, ParentClass); // Inherit the parent class prototype chain./** @return {string} */ChildClass.prototype.getName = function()
    {returnthis.name_;};

 /** @param {string} value */ChildClass.prototype.setName = function(value)
    {this.name_ = value;};

Down the rabbit hole

For those who are curious why that works vs simply memorizing it the inherits function.

How properties are resolved using the prototype chain

When a property is not found at the instance level, JavaScript will try to resolve the missing property by searching through the instance constructors prototype chain. If the property is not found in the first prototype object, it will search the parent prototype object and so on all the way up to Object.prototype. If it can't find it within Object.prototype then an error will be thrown.

Calling the parent constructor from child constructor : Attempt #1

// BadvarChildConstructor = function(arg1, arg2, arg3){
    var that = newParentConstructor(this, arg1, arg2, arg3);
    that.getArg1 = function(){return arg1};
    return that;
}

Any varible that is created using new ChildConstructor will return an instance of ParentConstructor. The ChildConstructor.prototype will be not be used.

Calling the parent constructor from child constructor : Attempt #2

// GoodvarChildConstructor = function(arg1, arg2, arg3){
    ParentConstructor.call(this, arg1, arg2, arg3);
}

Now constructor and the parent constructor is called appropriately. However, only methods defined within the constructor(s) will exist. Properties on the parent prototypes will not be used because they have not yet been linked to the child constructors prototype.

Inheriting the parent prototype : Attempt #1

// BadChildConstructor.prototype = newParentConstructor();

The parent constructor will either be called only once or one too many times depending on whether or not ParentConstructor.call(this) is used.

Inheriting the parent prototype attempt #2

// BadChildConstructor.prototype = ParentConstructor.prototype;

Though this technically works, any assignments to ChildConstructor.prototype will also be assigned to ParentConstructor.prototype because Objects are passed by reference and not by copy.

Inheriting the parent prototype attempt #3

// Almost therevarTempConstructor = function(){};
TempConstructor.prototype = ParentConstructor.prototype;
ChildConstructor.prototype = newTempConstructor();

This allows you to assign properties to ChildConstructor.prototype because it is an instance of a temporary anonymous function. Properties that are not found on the instance of TempConstructor will then check it's prototype chain for the property, so you have successfully inherited the parent prototype. The only problem is that ChildConstructor.prototype.constructor is now pointing to TempConstructor.

Inheriting the parent prototype attempt #4

// GoodvarTempConstructor = function(){};
TempConstructor.prototype = ParentConstructor.prototype;
ChildConstructor.prototype = newTempConstructor();
ChildConstructor.prototype.constructor = ChildConstructor;

All Together

varParentConstructor = function(){
};


varChildConstructor = function(){
    ParentConstructor.call(this)
};

varTempConstructor = function(){};
TempConstructor.prototype = ParentConstructor.prototype;
ChildConstructor.prototype = newTempConstructor();
ChildConstructor.prototype.constructor = ChildConstructor;

You've successfully inherited from the parent class! Let's see if we can do better.

The inherits function

functioninherits(childConstructor, parentConstructor){
    varTempConstructor = function(){};
    TempConstructor.prototype = parentConstructor.prototype; // Inherit parent prototype chain

    childConstructor.prototype = newTempConstructor(); // Create buffer object to prevent assignments directly to parent prototype reference.
    childConstructor.prototype.constructor = childConstructor; // Reset the constructor property back the the child constructor (currently set to TempConstructor )
};


varParentConstructor = function(){
};


varChildConstructor = function(){
    ParentConstructor.call(this)
};
inherits(ChildConstructor, ParentConstructor);

Post a Comment for "Javascript Prototypal Inheritance - Descendants Override Each Other"