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
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
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
}
If you put this on your browser's console and console log it you'll see something like this:
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:
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.
[...] 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;
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
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
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
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
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
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)
Thanks for commenting, @lukeshiru . Those are real good points!!!!