34

Assume I have a class like this:

function Widget() {
    this.id = new Date().getTime();
    // other fields
}
Widget.prototype = {
    load: function(args) {
        // do something
    }
}

From this class I created some other classes which inherit the same prototype but have some added methods. What I want to do is being able to define a load() method in the sub-classes which first calls the parent method and then execute some code. Something like:

SpecialWidget.prototype = {
    load: function(args) {
        super.load(args);
        // specific code here
    }
}

I know there's no super keyword in Javascript but there must be a way to do this.

Cœur
  • 37,241
  • 25
  • 195
  • 267
The Coding Monk
  • 7,684
  • 12
  • 41
  • 56

6 Answers6

43

You can simulate it like this:

SpecialWidget.prototype = {
    load: function(args) {
        Widget.prototype.load.call(this, args);
        // specific code here
    }
}

Or you can create your own super property like this:

SpecialWidget.prototype.parent = Widget.prototype;

SpecialWidget.prototype = {
    load: function(args) {
        this.parent.load.call(this,args);
        // specific code here
    }
}
karim79
  • 339,989
  • 67
  • 413
  • 406
3

so first, you set up your 'subclass' like so

function SubClass(name) {
    Super.call(this);

    // stuff here
}

SubClass.prototype = new SuperClass(null);
SubClass.prototype.constructor = SubClass;

and then you can do

SuperClass.prototype.theMethod.apply(this);

from within a subclass implementation to specifically invoke the super's implementation.

hvgotcodes
  • 118,147
  • 33
  • 203
  • 236
  • For those, like me, that questioned the difference between this approach and the call in the answer, the short answer is there is none. The long answer is provided by mozilla here... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply – Jackie Aug 20 '15 at 13:43
1

I don't know if this is the best solution, but you could do something like this:

function Widget() {
    this.id = new Date().getTime();
}
Widget.prototype.load = function(args) {
   alert( 'parent load' );
};

SpecialWidget = function(){};

   // Make the prototype of SpecialWidget an instance of Widget
var proto = SpecialWidget.prototype = new Widget;

   // Give the prototype a function that references the "load" from Widget
proto.parent_load = proto.load;

   // Give SpecialWidget its own "load" that first calls the parent_load
proto.load = function( args ) {
    this.parent_load( args );
    alert( 'special load' );
};

var inst = new SpecialWidget;

inst.load();

This makes the prototype of SpecialWidget an instance of Widget so that it inherits all that Widget has.

Then it makes a reference to the load() of Widget called parent_load(), and creates its own load() that calls the parent_load() when invoked.

user113716
  • 318,772
  • 63
  • 451
  • 440
1

Since mid-2015 (ECMAScript 2015), javascript has Classes and super

Here's the link: https://262.ecma-international.org/6.0/, see section 12.3.5 (super) and 14.5 (Class definitions).

How your code would look with those changes:

class Widget() {
    constructor() {
        this.id = new Date().getTime();
        // other fields
    }
    load(args) {
        // do something
    }
}

class SpecialWidget extends Widget {
    load(args) {
        super.load(args);
        // specific code here
    }
}

The closest I got to the previous syntax (without using class but using super) was using Object.setPrototypeOf:

// UNCHANGED
function Widget() {
    this.id = new Date().getTime();
    // other fields
}
Widget.prototype = {
    load: function(args) {
        // do something
    }
}

// slightly changed to declare SpecialWidget
function SpecialWidget() {}

// changed to define load as an method, and not a property with function as value
SpecialWidget.prototype = {
    load(args) {
        super.load(args);
        // specific code here
    }
}

// here's the key
Object.setPrototypeOf(SpecialWidget.prototype, Widget.prototype);

The declaration of load was changed because super can be used inside methods, but not functions. So, instead of load: function(args) { body }, it's simply load(args) { body }.

But, there's a caveat: with this solution, elements of SpecialWidget will not inherit the id defined as new Date().getTime(). I don't think there's a workahound (without using classes or duplicating code declaring this.id inside SpecialWidget).

user19513069
  • 317
  • 1
  • 9
0

Using Simple Javascript Class:

Class.extend('Widget', {
  load: function () {
    alert('foo');
  }
});

Widget.extend('SpecialWidget', {
  load: function () {
    this.super();
    alert('bar');
  }
});

new Widget().load(); // Alert: 'foo'
new SpecialWidget().load(); // Alert: 'foo' and 'bar'

Take a look at Simple Javascript Class Project, Simple JavaScript Inheritance and Inheritance Patterns in JavaScript.

Eduardo Cuomo
  • 17,828
  • 6
  • 117
  • 94
0

It would be possible to store the old value of the load method in a closure, if you did your overriding like this:

function Widget() {
    this.id = new Date().getTime();
    // other fields
}

Widget.prototype = {
    load: function(args) {
        // do something
        alert("Widget Prototype Load");
    }
};

function SpecialWidget(){
};

SpecialWidget.prototype = new Widget();

(function(){
    var oldLoad = SpecialWidget.prototype.load;
    SpecialWidget.prototype.load = function(){
        oldLoad();
        alert("new Load");
    };
}());


var x = new SpecialWidget();
x.load();

It works, but I'm not sure if it's the best method.

david
  • 17,925
  • 4
  • 43
  • 57