Saturday, November 2, 2024

Calling Parent Methods in JavaScript

One of the big differences between Classical (Object-Oriented) and Prototypal inheritance is that the former has an elegant mechanism for referring to the parent using the super keyword. It’s often used in constructors to initialize the parent class with the supplied input parameters. Another common usage is to extend parent functionality in the child class. In today’s article we’ll establish how to accomplish the same thing in JavaScript.

A common Inheritance pattern is to create an abstract – that is to say, a non-instanciable – class from which other classes will derive from. There are lots of examples in life: a person, a car, a house…anything that is too generic to represent a tangible representation of something qualifies. Often times, the generic object looks just fine until we subclass it into something more tangible. For instance, we might think that it’s easy to envision an instance of a person; give “it” a sex, age, height, weight, and it’s all good, at least until you need to separate children from adults, or seniors from non-seniors, males from females, etc… Reasons for doing so could be as numerous as they are persuasive. In many applications, it’s not enough to give a person a sex attribute; there are so many differences between men and women that it may be best to have a base class for each. Once that’s done, there should never be a need to instantiate a generic person again.

A similar case is that of animals. An animal seems like a plausible instance candidate at first, but once you start to divide them into mammals and birds, or quadrupeds and bipeds, you soon realize that a generic animal is just too broad a classification to be used effectively. Here is a constructor function for the generic Animal class. This won’t stop someone from instantiating it, but an alert can act as a reminder:

function Animal() {
  alert('You're not supposed to instantiate a generic animal!');
}
Animal.prototype = {
  name: 'noname',
  speak: function(food) {
    return 'I would like to eat '+food+'!';
  }
}; 

The prototype includes all the attributes and methods that will be inherited by subclasses.

A surefire way to make sure that no one instantiates an Animal is to declare it as an object straightaway. All of it’s members will be passed on to child classes automatically:

var Animal = {
  name: 'noname',
  speak: function(food) {
    return 'I would like to eat '+food+'!';
  }
}; 

The following code creates a Rabbit class which will be instantiated to create many, many rabbits (you know how rabbits are). It overrides the Animal’s speak() method so that rabbits no longer speak:

var Rabbit = function(name) {   
  if (name) this.name = name;  
  
  this.speak = function() {
    return 'Rabbits don't speak!';
  };
}

Rabbit.extend(Animal);
var rabbit1 = new Rabbit('Joe');
var rabbit2 = new Rabbit('Ken');
alert( rabbit1.speak() );  //displays "Rabbits don't speak!"
alert( rabbit2.speak() );  //also displays "Rabbits don't speak!"

We could have Rabbit extend Animal by creating a new Animal, but remember, we don’t want to instantiate it. Instead, the extend() function accomplishes the same thing by setting the Rabbit’s prototype to that of the Animal. The modus operandi to pass on member attributes from the parent to the child is to:

  1. Create an empty function.
  2. Set its prototype to the parent’s.
  3. Instantiate the object using the new keyword.

The following inherit() method encapsulates these three steps to return a new Rabbit:

function inherit(proto) {
  function F() {};
  F.prototype = proto;
  return new F;
}
var Rabbit = inherit(Animal.prototype) 

This modified version of the above function emulates the OO extend keyword and can be applied directly to the child constructor function (we used it with the Rabbit() function above):

Function.prototype.extend = function(parent) {
  var child = this;
  child.prototype = parent;
  child.prototype = new child(Array.prototype.slice.call(1,arguments));
  child.prototype.constructor = child;
}  


We can bypass the Rabbit’s speak() method by directly calling its parent’s:

var rabbit2 = new Rabbit('Rebel');
rabbit2.speak = function(food) {
  return Animal.speak.call(this, food);
}
alert(rabbit2.speak('a carrot'));  //displays "I would like to eat a carrot!"

Never call the parent method directly. It’s better to pass the this pointer along so that class references will still point to the child.

//in Animal:
sayHello: function() {
  return 'Hi, I'm ' + this.name + '.';
}
var rabbit2 = new Rabbit('Rebel');
rabbit2.sayHello = function() {
  return Animal.sayHello();
}
alert(rabbit2.sayHello()); //returns "Hi, I'm noname." wrong!


We can add a $super property to the child’s prototype to keep track of our parent. We can then use it like any other class variable by prefacing it with the this keyword:

Function.prototype.extend = function(parent) {
  var child = this;
  child.prototype = parent;
  child.prototype.$super = parent;
  child.prototype = new child(Array.prototype.slice.call(arguments,1));
  child.prototype.constructor = child
}

rabbit2.sayHello = function(food) {
  return this.$super.sayHello.call(this);
}


The $super pointer can be put to equally good use in the Rabbit class to override the parent method while adding a little extra.

var Rabbit = function(name) {   
  if (name) this.name = name;  
  
  this.speak = function() {
     return this.sayHello() + ' ' + this.$super.speak.apply(this, arguments);
  };
}
var rabbit = new Rabbit('Roger');
alert(rabbit.speak('a carrot'));//displays "Hi, I'm Roger. I would like to eat a carrot!"

 

 

Conclusion

Even without a super pointer, referencing a class’s parent is quite achievable. Now that we’ve covered how to obtain a reference to the parent class, we’ll explore how to accomplish method chaining in JavaScript.

Rob Gravelle
Rob Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured