140

Is it possible to call the base method from a prototype method in JavaScript if it's been overridden?

MyClass = function(name){
    this.name = name;
    this.do = function() {
        //do somthing 
    }
};

MyClass.prototype.do = function() {  
    if (this.name === 'something') {
        //do something new
    } else {
        //CALL BASE METHOD
    }
};
1
  • Which is the base method? this.do = function(){} in the constructor? Commented Feb 18, 2009 at 12:41

14 Answers 14

208

I did not understand what exactly you're trying to do, but normally implementing object-specific behaviour is done along these lines:

function MyClass(name) {
    this.name = name;
}

MyClass.prototype.doStuff = function() {
    // generic behaviour
}

var myObj = new MyClass('foo');

var myObjSpecial = new MyClass('bar');
myObjSpecial.doStuff = function() {
    // do specialised stuff
    // how to call the generic implementation:
    MyClass.prototype.doStuff.call(this /*, args...*/);
}
Sign up to request clarification or add additional context in comments.

7 Comments

I don't get this, i came here exactly for the same purpose as markvpc . I mean, i want to have a kind of a "private" method which is used from other methods in the prototype. Your response forces me to create a factory and i'd like to know if there's any way to avoid that.
Do not use "Class" words in JS because it creates confusion. There is not classes in JavaScript
This only works for objects instantiated. What if you want to override all instances?
Works for me! Exactly what I needed. I have 4 constructors that inherit from a parent. Two of them need to override and call a method on the base constructor that is their prototype. This allowed me to exactly that.
I don't understand why this has so many upvotes and is marked as correct. This only overrides the behavior for one instance of the base-class, not all instances of the sub-class. Thus this does not answer the question.
|
25

Well one way to do it would be saving the base method and then calling it from the overriden method, like so

MyClass.prototype._do_base = MyClass.prototype.do;
MyClass.prototype.do = function(){  

    if (this.name === 'something'){

        //do something new

    }else{
        return this._do_base();
    }

};

4 Comments

This will create infinite recursion.
I've been there: Infinite recursion.
The else clause is calling and running _do_base, which is a reference to prototype.do. Since no parameters (or state) have changed, the code will again go into the else, calling _do_base again.
@JohanTidén the _do_base stores a reference to whatever function was defined before. If there was none, then it’d be undefined. JavaScript is not make—expressions are never lazily evaluated like you suggest they are. Now, if the prototype being inherited from also used _do_base to store what it thinks is its base type’s implementation of do, then you do have a problem. So, yeah, a closure would be a better, inheritance-safe way to store the reference to the original function implementation if using this method—though that would require using Function.prototype.call(this).
17

I'm afraid your example does not work the way you think. This part:

this.do = function(){ /*do something*/ };

overwrites the definition of

MyClass.prototype.do = function(){ /*do something else*/ };

Since the newly created object already has a "do" property, it does not look up the prototypal chain.

The classical form of inheritance in Javascript is awkard, and hard to grasp. I would suggest using Douglas Crockfords simple inheritance pattern instead. Like this:

function my_class(name) {
    return {
        name: name,
        do: function () { /* do something */ }
    };
}

function my_child(name) {
    var me = my_class(name);
    var base_do = me.do;
    me.do = function () {
        if (this.name === 'something'){
            //do something new
        } else {
            base_do.call(me);
        }
    }
    return me;
}

var o = my_child("something");
o.do(); // does something new

var u = my_child("something else");
u.do(); // uses base function

In my opinion a much clearer way of handling objects, constructors and inheritance in javascript. You can read more in Crockfords Javascript: The good parts.

3 Comments

Very nice indeed, but you can't really call base_do as a function, because you lose any this binding in the original do method that way. So, the setup of the base method is a bit more complex, especially if you want to call it using the base object as this instead of the child object. I would suggest something akin to base_do.apply(me, arguments).
Just wondering, why don’t you use var for base_do? Is this on purpose and, if so, why? And, as @GiulioPiancastelli said, does this break this binding within base_do() or will that call inherit the this from its caller?
@binki I updated the example. The var should be there. Using call (or apply) allows us to bind this properly.
11

I know this post is from 4 years ago, but because of my C# background I was looking for a way to call the base class without having to specify the class name but rather obtain it by a property on the subclass. So my only change to Christoph's answer would be

From this:

MyClass.prototype.doStuff.call(this /*, args...*/);

To this:

this.constructor.prototype.doStuff.call(this /*, args...*/);

2 Comments

Don't do this, this.constructor might not always point to MyClass.
Well, it should, unless someone screwed up setting up the inheritance. 'this' should always refer to the top-level object, and its 'constructor' property should always point to its own constructor function.
6

if you define a function like this (using OOP)

function Person(){};
Person.prototype.say = function(message){
   console.log(message);
}

there is two ways to call a prototype function: 1) make an instance and call the object function:

var person = new Person();
person.say('hello!');

and the other way is... 2) is calling the function directly from the prototype:

Person.prototype.say('hello there!');

Comments

5

This solution uses Object.getPrototypeOf

TestA is super that has getName

TestB is a child that overrides getName but, also has getBothNames that calls the super version of getName as well as the child version

function TestA() {
  this.count = 1;
}
TestA.prototype.constructor = TestA;
TestA.prototype.getName = function ta_gn() {
  this.count = 2;
  return ' TestA.prototype.getName is called  **';
};

function TestB() {
  this.idx = 30;
  this.count = 10;
}
TestB.prototype = new TestA();
TestB.prototype.constructor = TestB;
TestB.prototype.getName = function tb_gn() {
  return ' TestB.prototype.getName is called ** ';
};

TestB.prototype.getBothNames = function tb_gbn() {
  return Object.getPrototypeOf(TestB.prototype).getName.call(this) + this.getName() + ' this object is : ' + JSON.stringify(this);
};

var tb = new TestB();
console.log(tb.getBothNames());

Comments

4
function NewClass() {
    var self = this;
    BaseClass.call(self);          // Set base class

    var baseModify = self.modify;  // Get base function
    self.modify = function () {
        // Override code here
        baseModify();
    };
}

Comments

4

An alternative :

// shape 
var shape = function(type){
    this.type = type;
}   
shape.prototype.display = function(){
    console.log(this.type);
}
// circle
var circle = new shape('circle');
// override
circle.display = function(a,b){ 
    // call implementation of the super class
    this.__proto__.display.apply(this,arguments);
}

1 Comment

Despite it may work, I won't recommend this solution. There are many reasons why using __proto__ (which changes the [[Prototype]]` of an object) should be avoided. Please use the Object.create(...) route instead.
2

If I understand correctly, you want Base functionality to always be performed, while a piece of it should be left to implementations.

You might get helped by the 'template method' design pattern.

Base = function() {}
Base.prototype.do = function() { 
    // .. prologue code
    this.impldo(); 
    // epilogue code 
}
// note: no impldo implementation for Base!

derived = new Base();
derived.impldo = function() { /* do derived things here safely */ }

Comments

1

If you know your super class by name, you can do something like this:

function Base() {
}

Base.prototype.foo = function() {
  console.log('called foo in Base');
}

function Sub() {
}

Sub.prototype = new Base();

Sub.prototype.foo = function() {
  console.log('called foo in Sub');
  Base.prototype.foo.call(this);
}

var base = new Base();
base.foo();

var sub = new Sub();
sub.foo();

This will print

called foo in Base
called foo in Sub
called foo in Base

as expected.

Comments

1

Another way with ES5 is to explicitely traverse the prototype chain using Object.getPrototypeOf(this)

const speaker = {
  speak: () => console.log('the speaker has spoken')
}

const announcingSpeaker = Object.create(speaker, {
  speak: {
    value: function() {
      console.log('Attention please!')
      Object.getPrototypeOf(this).speak()
    }
  }
})

announcingSpeaker.speak()

Comments

0

No, you would need to give the do function in the constructor and the do function in the prototype different names.

Comments

0

In addition, if you want to override all instances and not just that one special instance, this one might help.

function MyClass() {}

MyClass.prototype.myMethod = function() {
  alert( "doing original");
};
MyClass.prototype.myMethod_original = MyClass.prototype.myMethod;
MyClass.prototype.myMethod = function() {
  MyClass.prototype.myMethod_original.call( this );
  alert( "doing override");
};

myObj = new MyClass();
myObj.myMethod();

result:

doing original
doing override

Comments

0
function MyClass() {}

MyClass.prototype.myMethod = function() {
  alert( "doing original");
};
MyClass.prototype.myMethod_original = MyClass.prototype.myMethod;
MyClass.prototype.myMethod = function() {
  MyClass.prototype.myMethod_original.call( this );
  alert( "doing override");
};

myObj = new MyClass();
myObj.myMethod();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.