246

I've spent the last couple of hours trying to find a solution to my problem but it seems to be hopeless.

Basically I need to know how to call a parent method from a child class. All the stuff that I've tried so far ends up in either not working or over-writing the parent method.

I am using the following code to set up OOP in javascript:

// SET UP OOP
// surrogate constructor (empty function)
function surrogateCtor() {}

function extend(base, sub) {
    // copy the prototype from the base to setup inheritance
    surrogateCtor.prototype = base.prototype;
    sub.prototype = new surrogateCtor();
    sub.prototype.constructor = sub;
}

// parent class
function ParentObject(name) {
    this.name = name;
}
// parent's methods
ParentObject.prototype = {
    myMethod: function(arg) {
        this.name = arg;
    }
}

// child
function ChildObject(name) {
    // call the parent's constructor
    ParentObject.call(this, name);
    this.myMethod = function(arg) {
        // HOW DO I CALL THE PARENT METHOD HERE?
        // do stuff
    }
}

// setup the prototype chain
extend(ParentObject, ChildObject);

I need to call the parent's method first and then add some more stuff to it in the child class.

In most OOP languages that would be as simple as calling parent.myMethod() But I really cant grasp how its done in javascript.

Any help is much appreciated, thank you!

YemSalat
  • 19,986
  • 13
  • 44
  • 51

9 Answers9

272

ES6 style allows you to use new features, such as super keyword. super keyword it's all about parent class context, when you are using ES6 classes syntax. As a very simple example, checkout:

Remember: We cannot invoke parent static methods via super keyword inside an instance method. Calling method should also be static.

Invocation of static method via instance method - TypeError !

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  classMethod() {
    return super.classMethod() + ', too';
  }
}
console.log(Bar.classMethod()); // 'hello' - Invokes inherited static method
console.log((new Bar()).classMethod()); // 'Uncaught TypeError' - Invokes on instance method

Invocation of static method via super - This works!

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

console.log(Bar.classMethod()); // 'hello, too'

Now super context changes based on invocation - Voila!

class Foo {
  static classMethod() {
    return 'hello i am static only';
  }

  classMethod() {
    return 'hello there i am an instance ';
  }
}

class Bar extends Foo {
  classMethod() {
    return super.classMethod() + ', too';
  }
}

console.log((new Bar()).classMethod()); // "hello there i am an instance , too"
console.log(Bar.classMethod()); // "hello i am static only"

Also, you can use super to call parent constructor:

class Foo {}

class Bar extends Foo {
    constructor(num) {
        let tmp = num * 2; // OK
        this.num = num; // ReferenceError
        super();
        this.num = num; // OK
    }
}

And of course you can use it to access parent class properties super.prop. So, use ES6 and be happy.

NiRUS
  • 3,901
  • 2
  • 24
  • 50
Dmytro Medvid
  • 4,988
  • 3
  • 25
  • 29
  • 13
    @fsinisi90 I believe, the question isn't about about parent's class methods, but rather about parent's instance methods that just can't be called with super keyword as of ES6. – mcmlxxxiii Jun 25 '17 at 20:48
  • 1
    it works also for methods that are not static (tested with Chrome, with no transpiliing, not tryed the static keyword) – Gianluca Casati Mar 10 '18 at 13:15
  • 1
    Why does it need `super` to be called? Is there an equivalence in “old” JS? – 1252748 Aug 24 '18 at 01:39
  • 3
    super() has to be called in child class constructor before anything else. – user938363 Sep 20 '18 at 19:10
  • 2
    @GianlucaCasati: you can only use `super()` in static methods; looks like you used it in constructor. – ZzZombo Dec 16 '18 at 13:04
  • @user938363 super can be called anywhere in the constructor (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super#Using_super_in_classes) – Bogdan D Aug 03 '20 at 11:27
  • @mcmlxxxiii super support non-static methods as well (e.g. call of `sayHistory` here: https://googlechrome.github.io/samples/classes-es6/index.html ) – Bogdan D Aug 03 '20 at 11:30
  • @BogdanD Err, the page you linked to says "this.height; // ReferenceError, super needs to be called first!" – Peilonrayz Mar 27 '21 at 17:20
  • @Peilonrayz my comment was short in reply to another comment. So to make it more exact: `super()` can be used in the constructor before any reference to `this` – Bogdan D Apr 09 '21 at 13:41
241

Here's how its done: ParentClass.prototype.myMethod();

Or if you want to call it in the context of the current instance, you can do: ParentClass.prototype.myMethod.call(this)

Same goes for calling a parent method from child class with arguments: ParentClass.prototype.myMethod.call(this, arg1, arg2, ..) * Hint: use apply() instead of call() to pass arguments as an array.

Seanny123
  • 8,776
  • 13
  • 68
  • 124
YemSalat
  • 19,986
  • 13
  • 44
  • 51
  • 9
    If you want to call it in the context of the current instance, you have to do `ParentClass.prototype.myMethod.apply() or `ParentClass.prototype.myMethod.call()`, like you do with your constructor. – JMM Aug 07 '12 at 23:40
  • 4
    Just adding in that if you want to call with arguments, they go inside the apply or call function (`ParentClass.prototype.myMethod.call(this, arg1, arg2, arg3...);`) – Gershom Maes May 17 '15 at 11:47
  • 1
    I don't understand. If I call ParentClass.prototype.myMethod.call(this); from ChildObject's myMethod, I've got an error "Uncaught TypeError: Cannot read property 'call' of undefined". – zhekaus Mar 10 '16 at 20:58
  • @zhekaus, it would mean that you do not have `myMethod` on your class. – YemSalat Mar 12 '16 at 05:41
  • 4
    I am currently using this.myFun = function() { } to declare an object method,so call ParentClass.prototype.myFun.call(...) is not working, so i have to use CurrentClass.prototype.myFun.call(...). JS is ... a crap, we should use real OOP. – Loenix Apr 01 '16 at 18:34
  • One more convenient way is: `ParentClass.prototype.myMethod.bind(this)(arg1, arg2, ...)` – ceremcem Nov 09 '18 at 11:58
8

Well in order to do this, you are not limited with the Class abstraction of ES6. Accessing the parent constructor's prototype methods is possible through the __proto__ property (I am pretty sure there will be fellow JS coders to complain that it's depreciated) which is depreciated but at the same time discovered that it is actually an essential tool for sub-classing needs (especially for the Array sub-classing needs though). So while the __proto__ property is still available in all major JS engines that i know, ES6 introduced the Object.getPrototypeOf() functionality on top of it. The super() tool in the Class abstraction is a syntactical sugar of this.

So in case you don't have access to the parent constructor's name and don't want to use the Class abstraction you may still do as follows;

function ChildObject(name) {
    // call the parent's constructor
    ParentObject.call(this, name);
    this.myMethod = function(arg) {
    //this.__proto__.__proto__.myMethod.call(this,arg);
    Object.getPrototypeOf(Object.getPrototypeOf(this)).myMethod.call(this,arg);
    }
}
Redu
  • 25,060
  • 6
  • 56
  • 76
5

Here's a nice way for child objects to have access to parent properties and methods using JavaScript's prototype chain, and it's compatible with Internet Explorer. JavaScript searches the prototype chain for methods and we want the child’s prototype chain to looks like this:

Child instance -> Child’s prototype (with Child methods) -> Parent’s prototype (with Parent methods) -> Object prototype -> null

The child methods can also call shadowed parent methods, as shown at the three asterisks *** below.

Here’s how:

//Parent constructor
function ParentConstructor(firstName){
    //add parent properties:
    this.parentProperty = firstName;
}

//add 2 Parent methods:
ParentConstructor.prototype.parentMethod = function(argument){
    console.log(
            "Parent says: argument=" + argument +
            ", parentProperty=" + this.parentProperty +
            ", childProperty=" + this.childProperty
    );
};

ParentConstructor.prototype.commonMethod = function(argument){
    console.log("Hello from Parent! argument=" + argument);
};

//Child constructor    
function ChildConstructor(firstName, lastName){
    //first add parent's properties
    ParentConstructor.call(this, firstName);

    //now add child's properties:
    this.childProperty = lastName;
}

//insert Parent's methods into Child's prototype chain
var rCopyParentProto = Object.create(ParentConstructor.prototype);
rCopyParentProto.constructor = ChildConstructor;
ChildConstructor.prototype = rCopyParentProto;

//add 2 Child methods:
ChildConstructor.prototype.childMethod = function(argument){
    console.log(
            "Child says: argument=" + argument +
            ", parentProperty=" + this.parentProperty +
            ", childProperty=" + this.childProperty
    );
};

ChildConstructor.prototype.commonMethod = function(argument){
    console.log("Hello from Child! argument=" + argument);

    // *** call Parent's version of common method
    ParentConstructor.prototype.commonMethod(argument);
};

//create an instance of Child
var child_1 = new ChildConstructor('Albert', 'Einstein');

//call Child method
child_1.childMethod('do child method');

//call Parent method
child_1.parentMethod('do parent method');

//call common method
child_1.commonMethod('do common method');
CaptureWiz
  • 1,685
  • 1
  • 15
  • 15
4

In case of multiple inheritance level, this function can be used as a super() method in other languages. Here is a demo fiddle, with some tests, you can use it like this, inside your method use : call_base(this, 'method_name', arguments);

It make use of quite recent ES functions, an compatibility with older browsers is not guarantee. Tested in IE11, FF29, CH35.

/**
 * Call super method of the given object and method.
 * This function create a temporary variable called "_call_base_reference",
 * to inspect whole inheritance linage. It will be deleted at the end of inspection.
 *
 * Usage : Inside your method use call_base(this, 'method_name', arguments);
 *
 * @param {object} object The owner object of the method and inheritance linage
 * @param {string} method The name of the super method to find.
 * @param {array} args The calls arguments, basically use the "arguments" special variable.
 * @returns {*} The data returned from the super method.
 */
function call_base(object, method, args) {
    // We get base object, first time it will be passed object,
    // but in case of multiple inheritance, it will be instance of parent objects.
    var base = object.hasOwnProperty('_call_base_reference') ? object._call_base_reference : object,
    // We get matching method, from current object,
    // this is a reference to define super method.
            object_current_method = base[method],
    // Temp object wo receive method definition.
            descriptor = null,
    // We define super function after founding current position.
            is_super = false,
    // Contain output data.
            output = null;
    while (base !== undefined) {
        // Get method info
        descriptor = Object.getOwnPropertyDescriptor(base, method);
        if (descriptor !== undefined) {
            // We search for current object method to define inherited part of chain.
            if (descriptor.value === object_current_method) {
                // Further loops will be considered as inherited function.
                is_super = true;
            }
            // We already have found current object method.
            else if (is_super === true) {
                // We need to pass original object to apply() as first argument,
                // this allow to keep original instance definition along all method
                // inheritance. But we also need to save reference to "base" who
                // contain parent class, it will be used into this function startup
                // to begin at the right chain position.
                object._call_base_reference = base;
                // Apply super method.
                output = descriptor.value.apply(object, args);
                // Property have been used into super function if another
                // call_base() is launched. Reference is not useful anymore.
                delete object._call_base_reference;
                // Job is done.
                return output;
            }
        }
        // Iterate to the next parent inherited.
        base = Object.getPrototypeOf(base);
    }
}
weeger
  • 429
  • 2
  • 9
2

How about something based on Douglas Crockford idea:

    function Shape(){}

    Shape.prototype.name = 'Shape';

    Shape.prototype.toString = function(){
        return this.constructor.parent
            ? this.constructor.parent.toString() + ',' + this.name
            : this.name;
    };


    function TwoDShape(){}

    var F = function(){};

    F.prototype = Shape.prototype;

    TwoDShape.prototype = new F();

    TwoDShape.prototype.constructor = TwoDShape;

    TwoDShape.parent = Shape.prototype;

    TwoDShape.prototype.name = '2D Shape';


    var my = new TwoDShape();

    console.log(my.toString()); ===> Shape,2D Shape
Aessandro
  • 5,517
  • 21
  • 66
  • 139
1

There is a much easier and more compact solution for multilevel prototype lookup, but it requires Proxy support. Usage: SUPER(<instance>).<method>(<args>), for example, assuming two classes A and B extends A with method m: SUPER(new B).m().

function SUPER(instance) {
    return new Proxy(instance, {
        get(target, prop) {
            return Object.getPrototypeOf(Object.getPrototypeOf(target))[prop].bind(target);
        }
    });
}
StefansArya
  • 2,802
  • 3
  • 24
  • 25
ZzZombo
  • 1,082
  • 1
  • 11
  • 26
1

more flexible answer with classic js.

You define "_parent = A.prototype;" in the child class, then you can call parent's methods with apply:

class A{
    _msg='A';
    _msgOnlyA=' great from A';
    constructor(){
    }
    hello(){
        console.log('hello '+this._msg+', '+this._msgOnlyA);
    }
};
class B extends A{
    _parent = A.prototype;
    _msg='B';
    constructor(){
        super();
    }
    hello(){
        this._parent.hello.apply(this, arguments);
        console.log('hello '+this._msg);
    }
};
var b = new B();
b.hello();
gezdy
  • 3,292
  • 2
  • 14
  • 14
  • 2
    `A.prototype.hello.apply(this, arguments);` works if you want to skip a class in the chain, which is what I was looking for; thanks. However for your example it is easier to just call `super.hello()`. – Manstie Aug 09 '21 at 07:38
  • @Manstie What are you talking about? `super() is only valid in derived class constructors` – Michael Aug 31 '21 at 23:29
  • 1
    @Michael you are correct, which is why you call `super.hello()`. – Manstie Sep 01 '21 at 02:21
  • @Manstie Oh, weird. I was getting the above error when I tried to call super from a non-constructor function. Must have been a cockpit error though, because now I can't reproduce it. – Michael Sep 01 '21 at 20:25
0

While you can call the parent method by the prototype of the parent, you will need to pass the current child instance for using call, apply, or bind method. The bind method will create a new function so I doesn't recommend that if you care for performance except it only called once.

As an alternative you can replace the child method and put the parent method on the instance while calling the original child method.

function proxy(context, parent){
  var proto = parent.prototype;
  var list = Object.getOwnPropertyNames(proto);
  
  for(var i=0; i < list.length; i++){
    var key = list[i];

    // Create only when child have similar method name
    if(context[key] !== proto[key]){
      let currentMethod = context[key];
      let parentMethod = proto[key];
      
      context[key] = function(){
        context.super = parentMethod;
        return currentMethod.apply(context, arguments);
      }
    }
  }
}

// ========= The usage would be like this ==========

class Parent {
  first = "Home";

  constructor(){
    console.log('Parent created');
  }

  add(arg){
    return this.first + ", Parent "+arg;
  }
}

class Child extends Parent{
  constructor(b){
    super();
    proxy(this, Parent);
    console.log('Child created');
  }

  // Comment this to call method from parent only
  add(arg){
    return super.add(arg) + ", Child "+arg;
  }
}

var family = new Child();
console.log(family.add('B'));
StefansArya
  • 2,802
  • 3
  • 24
  • 25
  • @Michael this is just an alternative solution, there are another answer that may suitable for your use case if you scroll on this page. – StefansArya Dec 20 '20 at 01:19