NS Code Logo
PAWEŁ TRENDAMarch 16, 2022

JS Inheritance - Explained

JS inheritance

Undoubtedly, some parts of javascript can be confusing at times. Javascript was not designed to be a OOP language, however, we can still do a OOP style of coding with it. Unlike classical inheritance there are two patterns of prototypal inheritance:

  • The prototypal pattern of prototypal inheritance.
  • The constructor pattern of prototypal inheritance.

Unfortunately JavaScript uses the constructor pattern of prototypal inheritance. This is because when JavaScript was created, Brendan Eich (the creator of JS) wanted it to look like Java (which has classical inheritance).

But the model of inheritance is a bit different here. In prototypal inheritance objects inherit from other objects. Constructors never come into the picture. This is what confuses most people.

So, were classes in JavaScript added to just confuse people or actually make their lifes simplier?

Is there something wrong with prototypical inheritance? Today I would like to focus on that aspect and clarify the topic a little bit more.

How javascript tries to imitate classes and is there a better way?

What is a class?

Some parts of javascript can be confusing at times. Undoubtedly, inheritance is one of them. Classes were introduced in EcmaScript 2015 (ES6) to provide a cleaner way to follow object-oriented programming patterns.

JavaScript still follows a prototype-based inheritance model. Classes in JavaScript are syntactic sugar over the prototype-based inheritance model which we use to implement OOP concepts.

Thus the introduction of classes in JS made it easier for developers to build software around OOP concepts. It also brought in similarities to different OOP-based programming languages such as C++ and Java.

Before class keyword

function Car(brand, model, color, price) { this.brand = brand; this.model = model; this.color = color; this.price = price; } const car = new Car("Marker", "Blue", "$3"); console.log(car);

The above code shows a Car constructor function that has brand, model, color, and price properties. We are using the new keyword with the Car constructor to create an object car. Now let's say we want to add a new function to the Pen constructor. To do this we need to add the function into the prototype property of Car. Have a look at the showPrice function below:

function Car(brand, model, color, price) { this.brand = brand; this.model = model; this.color = color; this.price = price; } const car = new Car("Fiat", "126p", "yellow", 30000); Car.prototype.showPrice = function(){ console.log(`Price of ${this.name} is ${this.price}`); } car.showPrice();

So, yes, it was a little verbose and ugly looking.

After class keyword

class ClassCar { constructor(brand, model, color, price){ this.brand = brand; this.model = model; this.color = color; this.price = price; } drive() { console.log('Vroom!'); } }

Ok, it does look cleaner and less verbose, but is the class a real class or is it something else? lets find out:

console.log(typeof ClassCar); // function

O wow, so the class actually hides something else behind itself. The MDN’s statement:

Classes are in fact 'special functions' is a bit misleading. It’s more accurate to say that classes are syntax sugar for constructor functions.

So as we took a quick look under the hood and saw what is going on there, we can say that:

  1. function with the new keyword is a constructor function
  2. using new keyword binds car.proto to Car.prototype.

Prototype

Prototype allows JS engine to look up the the methods and properties we are trying to call on an object. The prototypical relations in JS create tree like structure where the root is Object.prototype. Thus, every object in JS inherits from the root which is the Object.prototype.

console.log(Object.getPrototypeOf({})==Object.prototype) // true console.log(Object.getPrototypeOf(Object.prototype)) // null

Let's check that deeper:

let empty ={} console.log(empty.toString) // ƒ toString() console.log(empty.toString()) // [object Object]

We were just able to call a method on an empty object. That is magic (for those who do not know od prototypical inheritance)

Let me explain it. First, javascript, in order to connect one object with another, has to create a 'link' or a 'reference' and how JS does it?

Really simple it actually attaches it as another property on an object.

Like so:

{} //our empty object [[Prototype]]:Object constructor:ƒ Object() hasOwnProperty:ƒ hasOwnProperty() isPrototypeOf:ƒ isPrototypeOf() propertyIsEnumerable:ƒ propertyIsEnumerable() toLocaleString:ƒ toLocaleString() toString:ƒ toString() //method we called valueOf:ƒ valueOf() __defineGetter__:ƒ __defineGetter__() __defineSetter__:ƒ __defineSetter__() __lookupGetter__:ƒ __lookupGetter__() __lookupSetter__:ƒ __lookupSetter__() __proto__ (get):ƒ __proto__() __proto__ (set):ƒ __proto__()

Here we see that even though we did not create property [[Prototype]] it was added there automatically. It is something that real class based language like java or c# does not have.

For javascript it is like an emergency source. When we try to call a method that does not exists on an object itself, JS engine goes up the prototype chain and looks for it there. So in this case, JS engine could not find it on our empty object, and instead went to a [[Prototype]] object and found it there.

And if it did not find it there it would throw an error:

console.log(empty.toNumber()) // Uncaught TypeError: empty.toNumber is not a function

Here we come to the essence of classes in javascript. In regular OOP languages class is just a type that is instantiated at runtime.

In JS, however, we have an actual instance of an object attached to our object and it is called you guess it :) a prototype.

Summary

Classes in javascript were added because it was thought that people from other programming languages can pick up JS quickly. In some way it is true, but it also added extra confusion in terms of which approach is better.

People not knowing how the prototypical inheritance work want to simply patch it with classes. But as we saw earlier, a class is just a plain old function :)

There is nothing wrong with prototypical inheritance, it is just the people responsible for JS development decided to go with the constructor pattern of prototypal inheritance. In the next article we will look how to create objects with factory functions.