How JS works, classes and prototypes!

How JS works, basically!

Introduction

Classes and Prototypes?

It sounds kind of cool!

Every coder has heard about classes, but I am not sure many developers have a deep understanding of prototypes.

Today, we will talk about classes and prototypes and we will get a better idea of how they work.

Also, this post is the last of the How Js works series, it took me a while, but with this post, the series is finalized yay!

JS Classes and Prototypes

The purpose of this post is to learn how we can organize our data in different ways.

We can start with the a basic example:

1:  const banana = {
2:      name: 'banana',
3:      amount: '2',
4:  }

Imagine we want to reduce the number of bananas, how can we do it?

An easy way that follows the principe of encapsulation will be to add the function directly on the object!

On the banana object, we add a property called decrease, and when we called it, the banana's amount will decrease by one.

1:  const banana = {
2:      name: 'banana',
3:      amount: '2',
4:      decrease: () => banada.amount--;
5:  }
6:  banana.decrease()

Another way to achieve this will be using the dot notation to create the same object.

1:  const apple = {};
2:  apple.name: 'apple';
3:  apple.amount: '2';
4:  apple.decrease: () => apple.amount--;

And there are even more ways to do this.

We can also use Object.create

1: const pinneaple = Object.create(null);
2: pinneaple.name: 'pinneaple';
3: pinneaple.amount: '2';
4: pinneaple.decrease: () => pinneaple.amount--;

Ok.

There are many ways to do it, but as you can see, for every piece of fruit, we also need to create every property, and it gets repetitive quickly, we are breaking the DRY principle.

Let's try to create fruits following the DRY principle.

Introducing Factory Functions.

We can create a function that will create an object for our fruity needs!

Welcome The fruitCreator™.

1:  function fruitCreator(name, amount){
2:      const newFruit = {};
3:      newFruit.name = name;
4:      newFruit.amount = amount;
5:      newFruit.decrease = function() { newFruit.amount-- };
6:      return newFruit;
7:  };
8:  const orange = fruitCreator('orange', 2);
9:  const cherry = fruitCreator('cherry', 1);

Do you see any problem with this method?

There are two big ones:

  • We still have to store the decrease function on each fruit object (not DRY!).
  • If we want to add a function, it will be added to any fruit, this is not a flexible approach!

So what would be a better way to create objects without having to store the same function on each object?

The Prototype Chain.

With the Prototype Chain, we can store the function on an object and have the interpreter look up to that object if the function is not found on the object created.

What?

Ok, let’s take a look...

The link we are talking about can be done by using the Object.create() technique, let's check it out.

1:  function fruitCreator(name, amount){
2:      const newFruit = Object.create(fruitFunctionStore);
3:      newFruit.name = name;
4:      newFruit.amount = amount;
6:      return newFruit;
7:  }; 
8:  
9:  const fruitFunctionStore = { decrease: function(){ this.amount-- } };
10: const orange = fruitCreator('orange', 2);
11: const cherry = fruitCreator('cherry', 1);
12: orange.decrease();

We create an object called fruitFunctionStore, on this object we assign a function to the variable decrease.

When on line 2, we create a new object with Object.create, even tho we are passing the function fruitFunctionStore, the object created will still be empty.

On line 12, when we call decrease, there is no reference to the decrease function on the local context of the orange object, so where does the execution context find it?

Since we pass the fruitFunctionStore object when using Object.create, a prototypal link has been created, this means that on the orange object, under the __proto__ property, you will find a link to the fruitFunctionStore object, where the execution context will find the decrease function.

Now any fruit created has, thanks to the __proto__ hidden property, a prototypal link to fruitFunctionStore. Only once fruitFunctionStore needs to be created, solving the issue we had before where we were storing the same function on every object!

When using Object.create() the parameter passed will be store on the __proto__ property of the object created.

Nice!

We should mention that this on this.amount, is an implicit parameter, when executing line 12, on the local memory of the execution context of decrease, the identifier this will automatically be assigned the value to the left of the function called, in this case, orange, so it becomes, orange.amount--.

What if there is even an easier way to achieve this.

Let´s take a look

The new keyword

The superpowers of the new keyword (it automates a lot of the work for us):

  • It creates an object.
  • It will create the prototypal link automatically.
  • It returns the object.
1:  function fruitCreator(name, amount){
2:      this.name = name;
3:      this.amount = amount;
4:  }
5:  fuitCreator.prototype.decrease = function(){ this.amount-- };  
9:  const orange = new fruitCreator('orange', 2);

To understand this, we need to clarify something, JS functions are both objects and functions, so we can store inside the prototype property our function fruitCreator.

When using the new keyword, the new object created orange will have on the __proto__ property a prototypal link to the fuitCreator.prototype.

This under the hood works in the same way as Object.create(fruitFunctionStore), but the new keywords do a lot of the work for us!

Some additional notes

Every object on JS has a link on the __proto__ value to the Object.prototype object, where there are functionalities stored like hasOwnProperty, useful to check if an object has a determined property.

When using new, the new object created will be directly assigned to this.

Conclusion

Well, this is the end of the How JS works series. I hope you enjoyed as much as I did!

Also, I honestly can't recommend enough Javacript the Hard Parts 2 by Will Sentance. If you are serious about learning JS, it is a must.

See ya!