Prototypes and Classes in JavaScript

Prototypes and Classes in JavaScript

In this blog we will understand prototypes, __proto__ property, "new" keyword, and classes in javascript.

Prerequisite

This blog assumes that you’re somewhat familiar with the objects and this keyword in javascript. If not, then please go through them once.

Introduction

Let's start with objects, so suppose we are building a game where each user object has a username, score, and increment method. So one way to create this object is to have a function that takes all the data creates a new object and returns that object.

It will look like this:

function userCreator(name) {
  const userObject = {}
  userObject.score = 0,
  userObject.username = name,
  userObject.incrementScore = function() {
    this.score++
  }
  return userObject
}

const user = userCreator('john')
console.log(user)  // {score: 0, username: 'john', incrementScore : ƒ}

This is okay for one or two users, but what about if we have N number of users, this won't be an efficient way to create a new user object, because if you see userCreator function carefully every time it creates a new object with that it also creates a new incrementScore method which is same for all the objects. So rather than creating a new incrementScore again and again for each object, can't we store it in one place where all the objects created using userCreator function can refer to.

Prototype chain

Using prototype chaining we can solve the above problem let's see. But before that let's dig into objects a little more.

In global memory, there is an Object constructor which is stored as a function + object representation.

image.png

Now all the functions in their object representation have the prototype property which itself is an object.

image.png

All the objects in javascript have a __proto__ property, which by default has a reference or link or chain to that main window.Object.prototype which is present in global.

image.png

image.png

So now let's come back to userCreator function, but this time will create the objects using Object.create().

Object.create() creates a new object using other object as its prototype.

So Object.create() takes null or another object, what it does is when an object is created using this method, that object's __proto__ which was by default pointing to the main window.Object kept in global, now will no longer point to that instead it will point to the object which we provided as an argument to Object.create().

KEY POINT: The argument given to the Object.create() is itself is an object whose __proto__ will point to the window.Object which is kept in global.

If we give null to the Object.create() as an argument then the object which is created, its __proto__ will no longer have any reference to the window.Object or any other objects.

image.png

Let's see an example when we give an object as a argument to the Object.create for userCreator fucntion:

const commonFunctionsObject = {
  increment: function() {
    this.score++
  }
}

function userCreator(name) {
  const userObject = Object.create(commonFunctionsObject)
  userObject.score = 0
  userObject.username = name
  return userObject
}

const user = userCreator('john')
console.log(user)  // {score: 0, username: 'john'}

But if we see the __proto__ of the user object then we can see that reference.

image.png

KEY POINT: So now if you see user object carefully then it shows [[Prototype]] where our commonFunctionsObject was stored. So the [[Prototype]] property is how the __proto__ link is indicated in javascript. And [[ ]] around Prototype indicates that it's an internal property that we can't access directly via user.Prototype

image.png

You can also see that there are two [[Prototype]], so what's that second one? You already know now, as I said commonFunctionsObject is an object whose __proto__ has a link to the main window.Object which is in global memory.

image.png

user -> commonFunctionsObject -> window.Object

So if we try to access the increment method on user then first it will look into itself, but it won't find it as it's not there only.

So javascript won't throw an error instead now it will go to its __proto__ link and check that object, in this case, it's commonFunctionsObject over there it will find the increment method and will execute it.

The lookup will look like user then commonFunctionsObject, and this is known as a Prototype chain, the chain which the object will follow to look for any property on it.

image.png

"new" keyword

What we see above, the way of creating objects using Object.create and then making there __proto__ point to the object that we pass as an argument to Object.create and then returning the newly created object is what new keyword automates for us.

Let's see an example:

function UserCreator(name, score) {
  this.score = score
  this.username = name
}

const user = new UserCreator("john", 0)
console.log(user)  // {score: 0, username: 'john'}

Now, what is this UserCreator, and how it is different from userCreator from the previous example?, so the first thing you see is the name UserCreator is basically a convention to name the function in capitalize way which indicates to developers that this function is used to create objects using new keyword.

UserCreator function is also called as constructor function.

So when we do const user = new UserCreator("john", 0) the UserCreator function will run:

  1. It first creates an empty object, and there is this which is by default present in the memory which will refer to the newly created empty object in the function's memory.
  2. Assigns all the values in the object that we pass and return the object.
  3. All the objects which are created now will have their __proto__ link to the UserCreator.prototype.

image.png

Now if we want to put our shared functions in this case increment, we can put them inside UserCreator.prototype.

function UserCreator(name, score) {
  this.score = score
  this.username = name
}

UserCreator.prototype.increment = function() {
  this.score++
}

const user = new UserCreator("john", 0)

image.png

If we create N users then all will refer to this same increment function.

image.png

Class

Classes were introduced in ECMAScript 2015 or ES6, now this is not a new thing, it's just syntactic sugar over what we just learned above for new keyword we can say a wrapper around that.

class UserCreator {
  constructor(name, score) {
    this.score = score
    this.username = name
  }

  increment() {
    this.score++
  }
}

const user = new UserCreator("john", 0)
console.log(user)  // {score: 0, username: 'john'}

Now we write our shared functions inside the wrapper itself i.e class and those functions will be stored in the UserCreator.prototype automatically.

image.png

So this is what all the class does.

Summary

  • All objects in javascript have the __proto__ property which by default has a link or a chain to window.Object.prototype.
  • Functions are stored as a function + its object representation in memory and all function's object representation have a default property called prototype.
  • new keyword automates the object creation and linking of their __proto__.
  • class is just a syntactic sugar / a wrapper. Rather than writing the shared functions in the constructor's prototype we directly write them inside the class which automatically stores is in the constructor's prototype.

References

That's it folks I hope you were now able to understand all this.

Connect with me on Twitter, GitHub, and LinkedIn.