OOP in JavaScript

Ashwin Kumar
4 min readJun 20, 2021

Objects are everywhere in JavaScript. Functions are Objects, Arrays are Objects. Hell even Classes are Objects, well not exactly. Classes are Functions but again Functions are Objects :D!

Welcome to Prototypal Inheritance:

JavaScript does not work the traditional way of using classes as blueprints to create objects, but rather objects as blueprints. Suffice to say this has hurt the sentiments of many. But how does it work?

Every object in JavaScript has got a Prototype chain attached it which is a set of objects linked with the internal hidden property [[Prototype]]. Every time a property or method lookup is done, the whole Prototype chain is scoured down from top to bottom to find the value which essentially is Prototypal Inheritance.

This prototype chain can be accessed using the property __proto__ which is a getter for the hidden Property [[Prototype]]. __proto__ is not usually used as it exists only for legacy reasons. If you want to access the prototype in your code, the better alternative is Object.getPrototypeOf.

Each new object created has Object.prototype in its prototype chain and each array has Array.prototype in its prototype chain which allows it to inherit methods like map(), filter() etc:

We can also add to this chain using the method Object.create() which allows us to create a new object with its [[Prototype]] set as the object passed to the method in the first @parameter:

Though Object.create() is very cool, it would be cooler if a function could do that thus giving us more flexibility in defining the object being created and that’s where constructor functions come into picture.

Every function defined in JavaScript with the keyword function has a property called prototype. This property refers to the container object that holds the defined function as its constructor:

Such functions then can be called with a new keyword which does the following:

  • A new object is created with its [[Prototype]] assigned to constructor function’s prototype object.
  • The this keyword inside the function points to the new object being created.
  • The created object is automatically returned by the function though it can be overridden by a custom return.

The above example can thus be recreated as this :

Classes in JavaScript:

A class is a much more standardised version of constructor Functions. It gives additional features like:

  • It can only be called with a new keyword, otherwise throws an error.
  • constructor and other methods can be directly added to constructor.prototype through class declarations.
  • static methods and properties can be added to the constructor using static keyword.
  • We can use extends keyword to extend the prototype chain.
  • Recent updates also allow private properties/methods to be added.
  • It runs in strict mode.

Above example can be written as a class like this:

Static methods and properties:

static methods and properties are actually added to the constructor function itself as the function is an object and objects can have its own properties.

This way these methods and properties can only be accessed through constructor function and can’t have direct access to instance methods and properties:

Extending a class:

A class extension is basically extending the prototype chain. Without using a class this can be done by:

  • Creating a constructor function and modifying its prototype object to inherit from the parent constructor.prototype to create the prototype chain.
  • calling the parent constructor function inside the new constructor function with right this reference so that new instance is instantiated by the parent constructor as well.
  • Adding the parent constructor function to the prototype chain of the new constructor function to make sure that all static properties/methods are inherited. This also ensures the right reference of super as we will see.

This in class would become:

super():

A super keyword is a special keyword which can only be used inside class declarations and Object literals. The reason being that it relies on an internal property [[HomeObject]] which points to the parent object of a method during method declaration.

The [[HomeObject]] may not always be the same as what this is pointing to in a method like calling the method from a different object or using function.bind(). Thus such methods using super makes them dependent on parent object.

The super keyword thus refers to the [[Prototype]] of [[HomeObject]] in a method:

Here getRoots() method has its [[HomeObject]] pointing to two and the super refers to [[Prototype]] of two which is set as one.

The super when called inside constructor refers to the parent constructor, however the super keyword can be used to access methods down the prototype chain as well.

But here’s the catch, in a class if super is called from static methods, the [[HomeObject]] points to the constructor function and thus super points to the parent constructor function as it’s set down the prototype chain by class. However for normal methods, the [[HomeObject]] points to constructor.prototype and thus super refers to parent constructor.prototype:

The important thing to note here is that super.atHome() can’t be accessed by method sleep() and super.sleep() can’t be accessed by method workingAt() .

Private Properties:

Encapsulation has been a long standing issue in JavaScript as there are no private or protected fields. The workarounds have usually been with underscored variables, Symbols or the mighty closures.

The recent updates however has the proposal moving forward for private class fields which can be used like this:

However if closures have to be used to achieve encapsulation, the one way of doing it is:

Conclusion:

OOP is a powerful programming paradigm which can be used to create self contained systems and the language gives us enough tools to successfully implement such systems. All I could do was give a tiny glimpse of the same.
Hope you liked it. Thanks for reading.

--

--

Ashwin Kumar

Web Developer, JavaScript Enthusiast, Love to Code.