All about inheritance in javascript

Pouria Morovati
Geek Culture
Published in
6 min readNov 21, 2021
Apple objects get their water from the root object. Image by Jen Theodore.

You have probably heard about Javascript constructors, prototype, prototype chain, and other scary terms like these. In this article, we will see what they are, why they exist, and how we can use them.

First, let’s see what a constructor is: A constructor or a “constructor function” is simply a function, which is called with new keyword! simple as that 😄

The keyword “new” calls a function as a constructor

So what? Why does this feature even exist? The answer is:

All objects in Javascript are created by constructors

Calling a function with the new keyword will always return an object. Javascript can be confusing especially for beginners because everyone learns that this is the way to create an object:

declaring a “regular” javascript object

Yes, this is the way that an object is created, but only an object of type Object!

The code above is a short-hand for this:

Curly braces are shorthand for new Object({})

See? There is no object in javascript that is not produced without a constructor. So what is Object? It is a built-in javascript function (constructor) that is responsible for creating “regular” objects. It is the root of all other objects in Javascript (We will discuss it later).

All data types in Javascript have a corresponding built-in constructor.

String, Number, Boolean, Object (also: Array, Function, …), …

So writing const myArray = [1, 2]; is a shorthand for writing const myArray = new Array(1, 2);.

Note: Do not use these constructors to create variables, Especially Function because it has security and performance issues.

Note: const myStr = "Hello"; isn’t a shorthand for const myStr = new String("Hello");. As you remember, the result of a constructor function call is always an object. So why do these constructors exist for primitives?

const myStr = "Hello!";
console.log(myStr.toUpperCase()); // HELLO!

Chaining a dot is only meaningful for variables of type “object” because they have structure. But the Javascript standard allows us to treat primitives like objects for convenience. So when we chain a dot to a primitive, the Javascript engine temporarily creates an object of the corresponding constructor internally and performs our property/method access on that temporary object. It is destroyed afterward.

Javascript wraps primitives in temporary objects when needed

The code above is a pseudo-code depicting what happens behind the scenes when we treat a primitive like an object.

How is a constructor different from a function?

Constructors are functions. the only difference is that when we call a function with the new keyword, javascript first creates a plain empty object, then passes this newly created object as a special variable called this to the function.

Note: It’s a good practice to name constructors as PascalCase in order to visually distinguish them from regular functions.

proof that “this” variable inside the constructor points to the returned object by “new”

As you can see, the variable this inside our constructor is exactly the same as the person variable. If two objects are equal, it means that they are pointing to the exact same location in memory, and mutating each one of them affects the other. So, adding properties to this inside PersonConstructor is exactly like adding the same properties to person.

Note: If a constructor function doesn’t return an object, it will implicitly return this (like our example which it is returning nothing, i.e. undefined).

Prototype

All Javascript functions have a special property called prototype. It is useful only when the function is called as a constructor, because when the new keyword creates the empty object, it adds an internal [[Prototype]] property to it that points to the constructor’s prototype property.

By default, prototype object only consists of one property called constructor that points to the constructor function itself.

person.__proto__.constructor === PersonConstructor; // true

Note: Writing a property name in double brackets means that it’s an internal property of that object and they are not available for the developers. To access this particular property on an object, we can use Object.getPrototypeOf(obj) method. Although it’s not standard, most browsers allow the developers to access it as a property named __proto__. From now on we will call it __proto__ for convenience.

What is __proto__ property anyway?

All object-oriented languages implement a method for inheritance. In Javascript, inheritance is done through this special __proto__ property. When you try to access a property/method on an object, Javascript engine first checks the object’s own properties, if it finds them, they will be used, if not, the object’s __proto__ will be checked. Again, remember this important relationship between __proto__ and prototype:

An object’s __proto__ property points to the “prototype" property on the constructor of that object.

the usage of object’s __proto__ property

The above diagram shows how Javascript looks for missing properties/methods. It checks that property/method on the prototype property of that object. But what if it is not there too? Remember, PersonConstructor.prototype is just another object! so if a property/method is missing in there, the above diagram is performed this time on Person.prototype.

When we create a function, its prototype property is created by the built-in Object constructor (i.e. plain object). So what do you expect the result of the following expression would be?

PersonConstructor.prototype.__proto__

Yes! It’s Object.prototyope.

prototype chain

This diagram shows something called “A Prototype Chain”. This is a mechanism for Javascript’s inheritance and how it goes up to find the missing property/method until it finally reaches null and therefore returns undefined.

To test the above diagrams, we can write this code:

proof of prototype chain

Note: This is an example to prove how the prototype chain works. Modifying prototype property of Javascript’s built-in constructors is generally a bad practice.

In the code above, sayHi doesn’t exist on person object. So let’s look at the prototype property of its constructor. sayHi also doesn’t exist on PersonConstructor.prototype. So let’s check the constructor of PersonConstructor.prototype which is Object. Yes, it exists in Object.prototype.

Wrapping Up

Javascript inheritance can be confusing at first. But it’s important to understand it. Here is a summary of what you need to know:

  • Constructors are functions, called with new keyword.
  • All Javascript objects are created by constructors.
  • The new keyword, creates an empty plain object and passes it to the constructor function as a variable called this.
  • If the constructor function doesn’t return an object, this will be returned implicitly.
  • The “regular” or “plain” object is created by the built-in Object constructor.
  • objects are aware of their constructor through a property called __proto__.
  • Property __proto__ on each object, points to its constructor’s prototype property.
  • prototype property exists on all javascript functions. By default, it is a plain object having only one property called constructor which points to the constructor function itself.
  • prototype property is just another object, it means that it has a __proto__ property too. So for a regular function, the constructor of its prototype property is Object.
  • If we want to access a property/method on an object which is missing on the object itself, Javascript looks at the __proto__ of the object (which is the prototype property of its constructor) and if it doesn’t also exist there, the same lookup is done for the __proto__ of the prototype object recursively until it either finds it or reaches null and returns undefined. This is called the “Prototype Chain”.
  • Since the property __proto__ on all instances of the constructor functions point to its prototype property, mutating prototype property on the constructor function is immediately available on all instances.

--

--