DEV Community

Cover image for Striping down the Prototype Chain
César O. Araújo
César O. Araújo

Posted on

Striping down the Prototype Chain

Sometimes I feel that a bunch of developers try to learn JavaScript but they only focus on top-level abstractions and they choose the path of learning the what it does, and not how it is done.

With that being said, let me ask you something: Do you know how something is evaluated as undefined under the hood?

If you don't know how it works, this article is for you, little Padawan.

It's all about prototypes

Whenever you see a class in JavaScript you're being lied about it. There's no such thing in JS. It is all about prototypes.

The ES2015 release brought the concept of "classes" into the world and the people use it a lot. Some of them don't know that classes are just a syntactical sugar. In the end, JS is a prototype-based language.

🔑 Prototype access on objects and functions

In order to add properties to objects you can use __proto__:

 const myObject = { x: 1 }

 myObject.__proto__.y = 2
Enter fullscreen mode Exit fullscreen mode

However if you intend to add properties to functions, you need to use the prototype access:

 const myFunction = function() { this.a = 1 }

 myFunction.prototype.b = 2
Enter fullscreen mode Exit fullscreen mode

The Prototype Chain

JavaScript has only one construct: Objects.

Each one of these objects has a private property called prototype. Prototype is also an object therefore it contains the prototype property on its own. This chain continues until the last prototype has the value of null.

Okay, but let me show you a practical example before you fall asleep:

// Create a real simple object
const myCustomObject = {
  x: 1,
  y: 2
}
Enter fullscreen mode Exit fullscreen mode

If you put this on your browser's console and console log it you'll see something like this:

Result of a console.log function showing the properties of an object and its prototype

As you can see, there's the expected properties from the object x and y plus the [[prototype]] object.

Try to expand this last one:

Prototype object expanded

There's a lot of things inside of it: a constructor, and some functions which can be used by this object. Each one of these have a prototype on its own. Expand them and check it out.

But again: the prototype itself has a __proto__ (same as [[prototype]]). Ok, let's expand it until the very end of the chain.

The prototype chain fully expanded

[...] and that's the end of the prototype chain. That's all, folks...

Just kidding, haha

Inheritance

Before we enter on the last topic of this article I would like to show you a little bit about how the inheritance happens with this prototype-based thing.

let myCustomFunction = function () {
   this.x = 1;
   this.y = 2;
}

// create a new object from myCustomFuncion()
// with its own properties
let instanceOfMyCustomFunction = new myCustomFunction(); 

// add two new properties to myCustomFunction's prototype
myCustomFunction.prototype.y = 3;
myCustomFunction.prototype.z = 4;
Enter fullscreen mode Exit fullscreen mode

Hm, right... Let's see how interesting all of this is.

  console.log(myCustomFunction.x); // 1
  console.log(myCustomFunction.y); // 2
  console.log(myCustomFunction.z); // 4
  console.log(myCustomFunction.t); // undefined
Enter fullscreen mode Exit fullscreen mode

Wait, wait, wait... y is not 3? how's that?! 👀

JS will look for a given property by checking the object properties first, then it will check the first prototype and so on until it finds the __proto__: null.

Check this out:

// JS finds the `x` property on myCustomFunction object
console.log(myCustomFunction.x); // 1

// JS finds the `y` property on myCustomFunction object
console.log(myCustomFunction.y); // 2
Enter fullscreen mode Exit fullscreen mode

Ok, but why the heck this is 2 and not 3? That's simple:

JS will return the first result found. In this case, 2 is the first value found.

If there was no y property on the base of the object, JS would search for it on object's prototype.

This is exactly what is happening with the z property:

// JS doesn't find the `z` property on myCustomFunction object
// so it searches on its prototype, where it's available
console.log(myCustomFunction.z); // 4
Enter fullscreen mode Exit fullscreen mode

Finally, when no value is found, JS assume that the property is not defined therefore the undefined value shows up:

// there's no `t` property whatsoever on the prototype chain.
console.log(myCustomFunction.t); // 4
Enter fullscreen mode Exit fullscreen mode

One last example

let myCustomFunction = function () {
   this.x = 1;
   this.y = 2;
}

myCustomFunction.prototype.foo = "bar"

let instanceOfMyCustomFunction = new myCustomFunction(); 

instanceOfMyCustomFunction.prop = "some value"


console.log(instanceOfMyCustomFunction.prop); // some value
console.log(instanceOfMyCustomFunction.foo); // bar
console.log(myCustomFunction.prop); // undefined
console.log(myCustomFunction.foo); // undefined
console.log(myCustomFunction.prototype.prop); // undefined
console.log(myCustomFunction.prototype.foo); // bar
Enter fullscreen mode Exit fullscreen mode

I hope this helps you to understand better what's going on under the JS' hood and if I missed something, let me know by commenting on this article!

Thanks for reading! See ya!

Top comments (1)

Collapse
 
oaraujocesar profile image
César O. Araújo

Thanks for commenting, @lukeshiru . Those are real good points!!!!