Inheritance in JavaScript

Inheritance in JavaScript

JavaScript is many things: a functional, dynamic, imperative and structured, etc; programming language. Heck, it can even be used for machine learning.

In this article, we would be looking at a concept on its object-oriented side: Inheritance.

Prerequisites

Before looking into inheritance, you need to know the object-oriented language paradigm as a prerequisite.

Check here if you want to know more about it or brush up on the concept.

Inheritance in JavaScript

Inheritance what is it about?

Imagine giving birth to a 12-year old son (yes, imagine it), you would notice that he has some behaviors and traits from you (skin and eye color, hair, eats a lot, etc.) and some other ones from your spouse. In other words, he inherited those characteristics from you two, and no one else, why? Because you and your spouse are his parents!

Everyone inherits a trait, personality, or property from someone or something. The same concept can be applied in programming. Inheritance in OOP is the ability of any class to acquire methods or properties from its parent class or base class. This is a powerful core concept in programming that allows programmers to create classes upon existing classes making your work and life easier. Cool isn't it? 😏

Now, inheritance in JavaScript is quite different than the normal OOP class-based inheritance you have in languages like Java or C#. Inheritance in JavaScript is prototype-based and not class-based (check this for more info), the class syntax introduced in ES6 is just syntactic sugar. However, it is easier to write, read, and understand!

Code Example

class Human {
  constructor(name) {
    this.name = name;
  }
  walk(place) {
    return `${this.name} is walking to ${place}`;
  }
  eat(food) {
    return `${this.name} is eating ${food}`;
  }
}
class Boy extends Human {
  constructor(name) {
    super(name);
  }
}


let akin = new Boy("Akin");


//inherited method
console.log(akin.walk("school"));

As you can see above the walk method was inherited by a boy named Akin, and the method was called with the akin object (akin.walk("school")), logging out his destination to the console with a description.

// Result logged out: Akin is walking to school

Again, the construct above (i.e class Human) does not make JavaScript class-based, as prototypes are still under the hood.

Classes can be and are used for more cleaner and easy-to-read code or maybe you're used to an object-oriented setting and you're having a hard time adapting. However, in some cases, using classes can lead to errors when using the this keyword on the constructor of classes as it may not perform as expected, hence, it's advised we use prototypes instead.

Read this for more info

Prototype Chain

Every object created by a constructor has an implicit reference (called the object’s prototype) to the value of its constructor’s prototype property. Furthermore, a prototype may have a non-null implicit reference to its prototype, and so on; this is called the prototype chain.

— ECMAScript 2019 Language

Every object in JavaScript has a private property that holds a link to another object called its prototype, and also has a prototype of its own, and so on until it reaches a null value. This null value is only reached when you try and access the prototype of the Object class.

Note: Almost all objects in JavaScript are instances of the Object class, except for primitives like Boolean, Strings, etc.

Here is what inheritance with the prototype chain looks like (please run on the browser console).

let Person = {
    name: "",
    age: 0,
    walk: function(){
        console.log("walking");
    }
}

let Micheal = Object.create(Person);
Micheal.name = "Hassan";

Micheal.walk = function(place){
    console.log(`Walking to ${place}`)
}
Micheal.work = function(work){
    console.log(`${this.name} works at ${work}`);
}

Micheal.name = "Micheal";
console.log(Micheal.__proto__);
console.log(Micheal.age);

/** 
Results:

 {name: "", age: 0, walk: ƒ}
 age: 0
 name: "" 
 walk: ƒ ()
 __proto__: 
   constructor: ƒ Object()
   hasOwnProperty: ƒ hasOwnProperty()
   isPrototypeOf: ƒ isPrototypeOf()
   ...

 0
**/

The first line creates an object called Person which has some default properties and a method. Next, I use Object.create to create an object Micheal from an existing object Person as the prototype of Micheal. Micheal, therefore, inherits all the default properties and functions in the Person object which I can now override with custom properties.

But be careful to override all needed properties from the Person object, lest if the property is called it will revert to its ancestor default property (which is 0 in our case!).

Note: If the JavaScript’s interpreter doesn’t find a specified property [like address] in the Micheal object; it searches through its ancestor for that property and if it is still not found, it searches through the ancestor prototype of that prototype and so on till it reaches an eventual null value.

Example: (Still using the Micheal object example above)

console.log(Micheal.__proto__);
console.log(Micheal.__proto__.__proto__);
console.log(Micheal.__proto__.__proto__.__proto__);

/**
Results:
  {name: "", age: 0, walk: ƒ}
  {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, 

}
  null
**/

Imagine you constantly use a particular function like reading from different files, you can easily create your own library built from prototypes and easily manipulate it anywhere in your code to suit your needs for any file type. Just inherit the properties and override any void methods if needed and you are good to go!

Thank you for reading this article on inheritance in JavaScript!

Naruto smiling gif

Useful Resources