All about inheritance in javascript
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 😄
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:
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:
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.
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.
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 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
.
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:
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 calledthis
. - 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’sprototype
property. prototype
property exists on all javascript functions. By default, it is a plain object having only one property calledconstructor
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 itsprototype
property isObject
.- 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 theprototype
property of its constructor) and if it doesn’t also exist there, the same lookup is done for the__proto__
of theprototype
object recursively until it either finds it or reachesnull
and returnsundefined
. This is called the “Prototype Chain”. - Since the property
__proto__
on all instances of the constructor functions point to itsprototype
property, mutatingprototype
property on the constructor function is immediately available on all instances.