A simplified overview of JavaScript prototypes and prototypical inheritance.
What are the two types of prototypes in JavaScript and what are they used for?
To understand prototypes and inheritance in JavaScript, you will need to understand these five points:
- Objects can have properties.
- Objects inherit properties from their prototype.
- Functions are objects which can also have properties.
- Functions have a prototype property.
- A function’s prototype is NOT the same thing as an object’s prototype.
Let’s examine each of these points in more detail.
Disclaimer: The information in this article is information that I have learned from Launch School, MDN, and this excellent video by a Launch School student called Ryan Schaul. I am a student at Launch School and I wanted to write an article that presents the basic concepts of prototypes in an easy-to-understand manner. Code snippets are linked at the bottom of the article.
Objects properties
The first thing you need to know is that objects in JavaScript can have properties. For example, the object foo
here has a property called bar
:
Its property can be accessed using dot notation as we see on line 5. On line 6 we verify that foo
has a property called bar
by calling the hasOwnProperty
method.
Object prototypes and inheritance
Objects usually have many properties that you can’t see by simply logging the value of the object. This is because objects can inherit properties from their prototype. The object that another object inherits properties from is called its prototype.
If you need a mental model, you can think of how a child inherits characteristics from its parent. In this example, the parent would be considered the ‘prototype’ and the child the ‘object’.
An object’s prototype is a private property that each object has that references or points to its prototype (the object that it inherits from). According to the JavaScript documentation at ECMAScript, this internal property is called [[Prototype]]
.
A good example of this is the method hasOwnProperty
on line 6. Notice that we call hasOwnProperty
using dot notation, which means that hasOwnProperty
is itself a property of an object. We prove that on line 7 by checking if Object.prototype
has a property called hasOwnProperty
which returns true
.
In this case, the object foo
inherits the property method hasOwnProperty
from Object.prototype
and can thus access the method. Object.prototype
can be thought of as being like the grandfather of all objects as almost all objects are descendants of Object.prototype
and therefore have access to its properties and methods. We will examine Object.prototype
in more detail later in this article.
To summarise:
- Objects have an internal property called
[[Prototype]]
that points to their prototypes. - Objects inherit properties and methods from their prototypes.
- Almost all objects are descendants of
Object.prototype
and have access to its properties and methods.
Object prototypes vs Function prototypes
There are two types of prototypes in JavaScript which are easily confused. As we have already seen, one is a private property that each object has called [[Prototype]]
that references that object’s prototype.
The other is a .prototype
property that almost all functions have that is only used when that function is invoked as a constructor. I say almost all because arrow functions and methods defined using the concise syntax do not have .prototype
properties and cannot be used as constructors. But what is a constructor function?
A constructor function is like an object factory that creates new objects which are instances of itself. You can see an example on line 6 of the following code block, where an object is implicitly returned by Factory
and a reference to that object is stored in the descendant
variable.
We can access Factory
's prototype property using dot notation, Factory.prototype
. Upon logging the value of Factory.prototype
on line 8 we see that it references an object. What is this object's purpose?
Each object instance that is created by Factory
inherits from the object referenced by Factory.prototype
. We can easily test this.
Notice that on line 6 we add a new property called example
to the object referenced by Factory.prototype
. We use Factory
to create a new object instance on line 9 and we store a reference to that object in the variabledescendant
.
When we then log its value on line 10, we see that the properties foo
and bar
have been assigned to it. Notice that we can also access the property example
through descendant on line 11 as it has inherited it from Factory.prototype
.
So what do we learn from this?
- Functions have a property called
prototype
which holds a reference to an object. - Any object instances created by a constructor function will inherit the properties of the object at
FunctionName.prototype
.
Function prototypes and Object.prototype
So where does the object referenced by Factory.prototype
come from? Interestingly it is an object that is a descendant of Object.prototype
.
On line 9 of the following code block we log the value of Factory.prototype
's prototype, which is the object referenced by Object.prototype
.
We confirm that on line 11 by showing that Object.prototype
and Factory.prototype
's prototype both reference the same object and on line 15 where we show that Object.prototype
is the prototype of Factory.prototype
.
We test this further by adding the property test
to Object.prototype
on line 16 which is the default object that almost all other objects inherit from. (This is not something you should do in your code, this is just an example that illustrates a point. I say almost all because it is possible to create an object that doesn’t inherit properties, but that is beyond the scope of this article).
Then on line 21 we can access that property through Factory.prototype
as Factory.prototype
is a descendant of Object.prototype
. We can also access the property test
from descendant
on line 23 as it inherits from Factory.prototype
which in turn inherits from Object.prototype
.
To summarize:
- A constructor function’s
prototype
property is a descendant of the object referenced byObject.prototype
.
Creating new objects
So what happens when you create a new object? When you create an object using the object literal syntax or using a constructor function, you are creating an object that inherits either directly or indirectly from Object.prototype
.
As the Object constructor is itself a function, it has a property called prototype
which can be accessed at Object.prototype
. Object.prototype
references an object, and almost all objects inherit properties from Object.prototype
. You can think of inheritance as a chain, each object’s prototype references the next link in the chain until it reaches the default object at the top of the chain, Object.prototype.
Notice in the following code block we initialize an empty object using the object literal syntax and store a reference to that object in the variable obj
.
On line 2 we log the value of Object.prototype
which references an object. On line 3 we log the value of obj
's prototype (or parent), which also references an empty object.
On line 5 we prove that the object referenced by Object.prototype
and the prototype of obj
are the same. And on line 6 we further prove that Object.prototype
is the prototype of obj
using the isPrototypeOf
method.
That means that if we mutate the object at Object.prototype
by giving it new properties, obj
will also be able to access those properties (Again this is not recommended, it’s just being used to illustrate a point here). On line 8 we initialize the property foo
of Object.prototype
to the string 'Hey there!'
.
We can test if the object has been mutated by logging the value of Object.prototype
, where we see that the property foo
has been successfully added. Now all descendants of Object.prototype
have access to foo
as we can see on line 11 when we access it from obj
.
What does this example teach us?
- Objects which are created using the object literal syntax inherit properties from
Object.prototype
.
Summary
So what have we seen?
- Objects have a private property called
[[Prototype]]
that points to that object’s prototype. - Almost all objects inherit properties and methods from their prototypes.
- Almost all objects are descendants of
Object.prototype
and therefore have access to its properties and methods. - Functions have a property called
prototype
which is a reference to an object. That object is a descendant of the object referenced byObject.prototype
. - Any object instances created by a constructor function will inherit the properties of the object at
FunctionName.prototype
.
There are of course ways to change an object’s prototype such as Object.create
or Object.setPrototypeOf
, but that’s for another article. This article has only scratched the surface of prototypical inheritance, but hopefully it has helped you to understand the basic concepts.
Thanks for reading!
As promised here is the link to the code snippets used in the article: https://gist.github.com/eamon0989