Understanding 'prototypes' and 'class'.

Subscribe to my newsletter and never miss my upcoming articles

To most early stage developers, especially the ones who come from a more strongly-typed background in coding, some foundational JS concepts can seem confusing at times, especially the more traditionally borrowed ones like this, class, Prototypes etc. I have dug deeper into the this keyword and some useful tips to eyeball it in my last post of this series.

Today, lets build on that and understand Prototypes and class a little better .

What is this and a this-aware function?

A function's this references the execution context for that call, determined entirely by how the function was called.

A this-aware function can thus, have a different context each time it's called, which makes it more flexible & reusable.

Consider the following two examples :

var school = { 
 teacher: "John",
 ask(question) {
  console.log(this.teacher, question);
 }
}
school.ask("what do you teach?");
// John what do you teach?
function ask(question) {
 console.log(this.teacher, question);
}

function someOtherClass() {
 var egContext = {
  teacher: "Peter",
 };
 ask.call(egContext, "how long have you been working here?");
}

someOtherClass();

The first example above shows how we can implicitly bind a this context and the latter shows an example of an explicitly bound one.

Prototype.. huh..wha?

It's nothing too fancy, I promise you. Let's build on an example together.

Say we wanted to create something similar to a class that we could instantiate later as required. We could do that by defining a function like School above, and making it this-aware. This function is going to act like a constructor for the instances of this so-called School class.

function School(teacher) {
 this.teacher = teacher;
}

Now, if we wanted to add methods to School, we could simply chain them to the .prototype of the School constructor, as we have done with the ask() method below.

School.prototype.ask = function(question) {
 console.log(this.teacher, question);
}

Note: If you did School.ask() instead of School.prototype.ask(), that would only chain .ask() to the School itself and not to any other instances of the so-called School class, which defeats our purpose of trying to build an encapsulating entity that could be instantiated later. Here's an example to demo this:

School.ask =  function(question) {
 console.log(this.teacher, question);
}

var inst = new School("John");

// School ->  function(teacher) {
//                   this.teacher = teacher;
//            }
//
// School.ask -> function(question) {
//                   console.log(this.teacher, question);
//           }
//
// inst.ask  -> undefined
// inst.ask("Why?")  -> Uncaught TypeError: inst.ask is not a function

Now, with that out of the way, let's continue building further and create two instances of School.

var exOne = new School("John");
var exTwo = new School("Peter");

exOne.ask("what do you teach?");
// John what do you teach?

exTwo.ask("how long have you been working here?");
// Peter how long have you been working here?

In a way, you could look at the prototype as an object where any instances are going to be linked or delegated to. As an example, when we say exOne = new School("John"), the new keyword is going to invoke the School function we defined above, and the object that gets created is going to be linked to School.prototype.

And since School.prototype has an ask() method, we can take the exOne instance and call ask() on it.

It is important to note that exOne as an object does not have an ask() method, but the code above works just fine, and we say the instance exOne is prototype linked to School.prototype. And since, we also made ask() a this-aware function, we see the output to be John what do you teach?

This, in a very brief nutshell, is prototypal class pattern.

The class keyword

So now that we are armed with our understanding of this keyword and the prototype system, let's dig deeper into what happens under-the-hood when we use class keyword in JS.

class is layered on top of the prototype system. It basically gives us a less awkward and a more cleaner implementation of the prototypal style.

Let's tweak the aforementioned example, to see the class implementation:

class School { 
 constructor(teacher) {
  this.teacher = teacher;
 }
 ask(question) {
  console.log(this.teacher, question);
 }
}

var exOne = new School("John");

exOne.ask("what do you teach?");
// John what do you teach?

So with this type of implementation, we use the class keyword and then inside the body of the function, we have a constructor() which contains everything from the function School() above. To add other methods like ask(), we put them directly inside the class, with their respective definitions. This syntactical sugar makes the code much cleaner and improves readability.

It's important to note here that under the covers, JavaScript is doing the exact same thing as what we saw earlier with the prototypal implementation.

Conclusion:

To be completely honest, the prototypal implementation is increasingly becoming rare to spot in people's codes, but I still believe, understanding the basic concepts and how Javascript works behind the curtains, can really help build a stronger foundation and give you the confidence to build great products. Hope you could take something from it. Happy coding!

Utkarsh Bhimte's photo

This is very helpful. thanks Tanvi for sharing this.

Luiz Filipe da Silva's photo

Totally agree! Undertanding how it works in the background is very important to know what exacly we are doing. Thanks for sharing!

Tanvi Priyadarshini's photo

My pleasure :) And yes totally! I my early stages, I had struggled to understand what exactly was going on. Hoping this helps everyone on some level.