CODEX

The prototype chain: how JavaScript really works

Adam Reeves
CodeX
Published in
5 min readFeb 19, 2021

--

If, like me, your previous programming experience was in Object Oriented languages like Java or C#, JavaScript’s prototype based approach to inheritance might take a minute to get your head around.

You can get by just fine without delving too deeply, or even thinking about prototypes. Where it starts getting more interesting though, is when we throw constructor functions and classes into the mix.

So, let’s dive in 🤿

What is a prototype?

Every object in JavaScript can be linked to a prototype object which is the mechanism through which inheritance is provided.

You can see the prototype an object is pointing to using the __proto__ property.

As a side note: there is some controversy around using __proto__ and it’s actually deprecated in favour of Object.getPrototypeOf() . You can read more about it here.

When a property or method is called on an object, if the object doesn’t contain it, the JavaScript engine will then look to the object’s prototype to resolve the property or method.

You may have seen the term prototype chain before. This is where the prototype of an object itself can also contain a prototype and so on.

If an object doesn’t contain a property or method, the JavaScript engine will continue up the prototype chain to try and resolve it.

Constructor functions

You may have seen some functions in JavaScript that are called with the new key word, for example new Date() — these are constructor functions. Keeping in theme with the rest of the article, here’s what a constructor function might look like and how it can be used:

function Swan(colour, mood) {
this.colour = colour;
this.mood = mood;
}
let swan = new Swan('white', 'angry');

The above code block creates an object, swan using the constructor function Swan with the properties colour and mood set to 'white' and 'angry' respectively. Notice the convention of capitalising constructor functions.

You may have noticed the use of the this keyword and if you’ve spent much time with JavaScript or other Object Oriented languages you’ve probably seen it used before. Be careful though, it might not behave how you expect in JavaScript compared to OO languages like Java.

this

We won’t go into too much detail on this as it’s one of the more confusing areas of JavaScript associated with binding and could easily have a post dedicated to it (let me know if you’d find this helpful!).

To cut a long story short, the value of this can vary depending on where a function is called.

When used in the context of a constructor function like the above example, the JavaScript engine creates an internal this context which is bound to the new object being created using the constructor function — swan in our case.

Inheritance using the prototype chain

In JavaScript, all functions have a Prototype property and all objects have a __proto__ property that points to the prototype of their constructor function.

The __proto__ property can be thought of as pointing to the prototype of the function it was created from.

Remember, we can also obtain the prototype of an object using Object.getPrototypeOf(yourObject) and it’s better practice to do so.

Let’s explore our previous example a bit more:

function Swan(colour, mood) {
this.colour = colour;
this.mood = mood;
}
let swan = new Swan('white', 'angry')Swan.prototype.isSassy = () => true;

The Swan has a prototype property that we add the isSassy() function too.

When we create the swan object using the Swan constructor function, this means the swan object’s __proto__ property will point to the prototype of Swan .

Give it a go. You can copy and paste the above code snippet and view the prototype of swan using swan.__proto__ . This will return:

> swan.__proto__
{isSassy: ƒ, constructor: ƒ}
isSassy: () => true
constructor: ƒ Swan(colour, mood)
__proto__: Object

The constructor of swan.__proto__ tells us that it was constructed using the Swan function and we can also see that the isSassy() function is on the prototype swan.__proto__is linking to as expected.

One useful features of an object created with a constructor is it allows us to check it’s type:

swan instanceof Swan //returns true

MDN defines the instanceof operator as follows:

The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value.

Applying that to our example:

The Swan constructor has the following prototype:

Swan(colour, mood) {
this.colour = colour;
this.mood = mood;
}

and swan.__proto__ has the following constructor property as we previously demonstrated:

Swan(colour, mood) {
this.colour = colour;
this.mood = mood;
}

Therefore, as the Swan prototype appears in the prototype chain of swan the boolean true is returned.

One important thing to note is that the entire prototype chain is checked. Bear this in mind if you have more complex prototype chains and also, remember that by default all new objects __proto__ property point to Object.prototype in their prototype chain so checking if <arbitraryObject> instanceof Object will always return true.

Classes in JavaScript aren’t what you think they are

So now we’ve covered the fundamentals of prototypes and constructors there’s one more thing I wanted to talk about in this post.

When I first started working with JavaScript professionally, fresh out of university where a lot of time is spent on widely used Object Oriented languages (mainly Java in my case), it’s incredibly easy to see classes in JavaScript and start connecting the dots.

In Object Oriented languages such as Java, a class generally serves as a blueprint and — excluding any static behaviour — whenever a new object is instantiated, it is instantiated as its own copy and occupies its own space in memory.

In JavaScript however, a class is just syntactic sugar and is still in fact, a function. When a class is ‘instantiated’, the new object actually has a prototype that is pointing to the class methods (which are in fact, properties on a prototype!) and that contains a constructor referencing it— sound familiar?

To demonstrate my point, look at the following:

class RubberDuckClass {
constructor(colour) {
this.colour = colour
}
doesFloat() {
return true;
}
}
function RubberDuckConstructor(colour) {
this.colour = colour;
}
RubberDuckConstructor.prototype.doesFloat = () => true;let rubberDuckClassObject = new RubberDuckClass('yellow');
let RubberDuckConstructorObject = new RubberDuckConstructor('blue');

In this example, we can actually prove that a class is in fact nothing more than a function wearing fancy clothes

rubberDuckClassObject:

  • has a colour property set to argument (‘yellow’)
  • prototype:
    - constructor references RubberDuckClass
    - has doesFloat() function

rubberDuckConstructorObject:

  • has a colour property set to argument (‘blue’)
  • prototype:
    - constructor references RubberDuckConstructor
    - has doesFloat() function

Although on the surface, JavaScript classes look like they’re behaving as we’d expect in the conventional class based design pattern; under the hood, classes are still nothing more than functions with inheritance capabilities provided by the prototype mechanism.

And, in my opinion, this is why classes in JavaScript can be misleading. Eventually a time will come when you as a developer will need to dig a bit deeper into the language, and it’s at that point you’ll be in for a shock when you discover classes might not be what you thought they were.

--

--

Adam Reeves
CodeX

Professional Software Engineer, man of many hobbies