What the heck is Prototypal Inheritance? Part 1: Object Prototypes
Saunved
Posted on July 2, 2022
Photo by Holly Stratton on Unsplash
Before you dive in, you should have a decent understanding of Objects and Functions in Javascript.
Prototypes and prototypal inheritance in Javascript can be confusing, especially if you have learned Object Oriented Programming from the perspective of Java or C++.
These posts aim to clear up some of the confusion, and make prototypal inheritance easier to understand.
Let's start!
First off, there are two "kinds" of prototypes in Javascript:
- Object prototypes
- Function prototypes
Although they both have similarities, it's best to treat them separately. In this article, we will be looking only at Object Prototypes.
Some facts I need you to keep in mind while reading this article:
- Any data type in Javascript that is not a primitive is an Object
- By extension, functions are technically objects in Javascript, albeit with some extra gimmicks, e.g. you can invoke them. MDN reference
- Objects can contain functions as their properties (keys). When functions are part of an object, they are called methods. Methods act on an object.
// For example
const person = {
name: 'Luke',
hello(){
console.log(`Hello, I am ${person.name}`)
}
}
person.hello(); // Prints 'Hello, I am Luke'
The this keyword is deliberately not used in the above example to avoid confusion.
Alright so...
What is an object prototype?
Let's create an object:
const character = { hearts: 5 }
When you create an object in Javascript, it contains the properties you have defined (in this case character.hearts
). However, this object also has some hidden properties, magically obtained from the Javascript Gods.
For example, if you were to do character.toString()
, it will print "[object Object]"
. You didn't define any toString()
method inside character
. So where the heck did it come from?
Enter the prototype
Follow along with the steps below to explore this interesting phenomenon:
(1) Open your console (in any browser, I am using Firefox) by pressing F12 > then click on "Console"
(2) Type and enter window.Object.prototype
. It looks like this:
We can see that this is an object with a bunch of predefined properties and methods.
(3) Type const character = {hearts: 1}
and press enter
(4) Type character
and press enter. The output looks like this:
(5) Type character.__proto__
and press enter. The output looks like this:
Keep the console open, and continue reading.
Do the objects at steps (2) and (5) look the same to you, despite being a part of different objects?
Well guess what, they are the same. And we can prove it.
Do this:
(1) Enter character.toString()
, and observe the output
(2) Now we will redefine the toString()
the method defined in window.Object.prototype
. Enter this in the console:
window.Object.prototype.toString = () => {console.log('Proved it')}
(3) Now, enter character.toString()
again and check the output
You modified a method that was owned by window.Object.prototype
and somehow, like spooky action at a distance, your character.toString()
method's output changed. Crazy stuff right?
This is a good time to pause and review the steps we performed above.
Time to understand this whole gimmick in depth.
All objects contain a prototype property. This "prototype property" is NOT available at character.prototype
. It is, instead, called __proto__
and is available as character.__proto__
.
The word "prototype" can be confusing. To make things easier to process, you can think of it as "parent" instead. So when you see character.__proto__
, you can think of it as "parent of character
".
What do you think will be the parent of character.__proto__
?
Well, you can chain these! So you can do: character.__proto__.__proto__
. This time, you will get null
.
null
is the ultimate ancestor of all Objects.Note:
__proto__
is not the standard way to access Object prototypes. The standard way is to doObject.getPrototypeOf(obj)
, but things are easier to write, explain, and understand when using__proto__
. Do not use this in production!
The following diagram should help clear a few things up.
As you can see above:
- All Objects in Javascript inherently are nothing, denoted by
null
- From this nothingness, arises something - the most basic object you can have -- an object with some default properties, e.g.
toString()
,valueOf()
. This primal object can be found atwindow.Object.prototype
and is defined by the browser - When you create your own object (any object at all, even an empty object), it inherits the properties of this basic object.
- So if you try to call
character.toString()
, Javascript checks if the method exists on the objectcharacter
. If not, it drills down and checks the parent ofcharacter
, which in this case is thewindow.Object.prototype
object.
According to the Oxford Languages dictionary, the word "prototype" means:
The first model or design of something from which other forms will be developed.
It's a succinct definition that applies well to the above behavior of prototype. It starts off as nothing and can progressively build up.
Posted on July 2, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.