DEV Community

Cover image for What the heck is Prototypal Inheritance? Part 1: Object Prototypes
Saunved
Saunved

Posted on

What the heck is Prototypal Inheritance? Part 1: Object Prototypes

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:

  1. Object prototypes
  2. 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:

  1. Any data type in Javascript that is not a primitive is an Object
  2. By extension, functions are technically objects in Javascript, albeit with some extra gimmicks, e.g. you can invoke them. MDN reference
  3. 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'
Enter fullscreen mode Exit fullscreen mode

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 }
Enter fullscreen mode Exit fullscreen mode

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:
window.Object.prototype console output
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:
character console output
(5) Type character.__proto__ and press enter. The output looks like this:
character.__proto__ console output

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
character.toString() console 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')}
Enter fullscreen mode Exit fullscreen mode

(3) Now, enter character.toString() again and check the output
character.toString() after modification

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 do Object.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.
Prototypal inheritance in Javascript

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 at window.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 object character. If not, it drills down and checks the parent of character, which in this case is the window.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.

Discussion (0)