Search code examples
javascriptinheritancemethodsprototypeextend

Extend inherited prototype method in Javascript


I have some classes (constructors) that are related as "parent-child":

// Abstract root SuperClass

function SuperClass() {
    this.CLASS_ID = "SUPER-CLASS";
    throw new Error('Failed to instantiate abstract class' + this.CLASS_ID);
}

SuperClass.prototype.sayHello = function() {
    alert('Hello, world!');
};

// Absctract ClassA inherits from SuperClass
// inherit() and extend() are generic functions from the David Flanagan`s 
// brilliand book "Definitive Guide"

function ClassA() {
    this.CLASS_ID = "CLASS-A";
    throw new Error('Failed to instantiate abstract class' + this.CLASS_ID);
}

ClassA.prototype = inherit(SuperClass.prototype);

// Concrete ClassB inherits from ClassA

function ClassB(initData) {
    this.CLASS_ID = "CLASS-B";
}

ClassB.prototype = inherit(ClassA.prototype);

extend(ClassB.prototype, {
    constructor: ClassB,

    welcomeNewDay: function() {
        this.sayHello();
        this.doSomethingElse();
    },

    doSomethingElse: function(){
        alert('Jumping');
    }
});

var classB = new ClassB();
classB.welcomeNewDay();

How can I correctly extend method .sayHello() of the abstract ClassA without overloading it?

I`ve tried to make it this way:

extend(ClassA.prototype, {
    sayHello: function() {
        this.__proto__.sayHello();
        this.sing();
    },

    sing: function() {
        alert('Lalala');
    }
});

The problem is that .sing() is invoked 3 times instead of 1.

If I try:

this.__proto__.sayHello.call(this);

It throws an exception:

Uncaught RangeError: Maximum call stack size exceeded


Solution

  • Try to access the initial class:

    extend(ClassA.prototype, {
        sayHello: function() {
            SuperClass.prototype.sayHello.call(this);
            this.sing();
        },
    
        sing: function() {
            alert('Lalala');
        }
    });
    

    or just store the current sayHello() method:

    var initialSayHello = ClassA.prototype.sayHello;
    extend(ClassA.prototype, {
        sayHello: function() {
            initialSayHello.call(this);
            this.sing();
        },
    
        sing: function() {
            alert('Lalala');
        }
    });
    

    You need to have the reference to the original sayHello() method.


    Uncaught RangeError: Maximum call stack size exceeded

    It is thrown because you actually have an infinite recursion, calling the method itself in the method.