DEV Community

Cover image for Understanding the new keyword in JavaScript
G U BHARATH CHANDRA
G U BHARATH CHANDRA

Posted on

Understanding the new keyword in JavaScript

Hey 👋 this will be a bit long , but detailed post where we will try to understand using an example.

Assume we have to build a game where users can register with username against which score would be updated. So, the below code would do the job right?

function generateUser(name,score){
  let newUser = {};
  newUser.name = name;
  newUser.score = score;
  return newUser;
}
Enter fullscreen mode Exit fullscreen mode

When ever we need to create a new User, we can use generateUser("<User entered name>" , 0) which would return us the new user Object.

So, the next thing we need to take into consideration is to increment the score if the user wins the game.

Obviously, the immediate choice would be to create a new function as below :

function incrementScore(user){
  user.score++;
  console.log(user);
}
Enter fullscreen mode Exit fullscreen mode

and whenever we need to increment the score, we can just use this function incrementScore(user1);

But the problem here is that, when our code is modular/grows into huge codebase in future, we might not know where the increment function is located and also, in a case where we might need other operations like changing the name or adding a new property , we cannot just keep creating new functions as that would clutter the codebase and would become difficult to maintain.

So , how do we solve that?

Well, in 1 word → prototype.

But lets slowly try to solve this problem by understanding each step.

What if we could just put the functions inside out generateUser as below?

function generateUser(name,score){
  let newUser = {};
  newUser.name = name;
  newUser.score = score;
  newUser.incrementScore = function() {
    newUser.score++;
    }
  return newUser;
}
Enter fullscreen mode Exit fullscreen mode

By this we can achieve the below :

let user1 = generateUser('BruceWayne',10);
user1.incrementScore();
Enter fullscreen mode Exit fullscreen mode

Perfect! , now we can just use the user Object itself to incrementScore or change name or whatever...

But, let's consider there are 200 users, in which case it is really painful/in-efficient memory usage to save the incrementScore function for every user when we know all it does is incrementScore by 1 and this operation is same across all users.

Turns out we can further optimize this approach using Object.create() method as below :

function generateUser(name,score){
  let newUser = Object.create(userFunctionsStore);
  newUser.name = name;
  newUser.score = score;
  return newUser;
}

let userFunctionsStore = {
  increment: function() { this.score++ ;}
}

let user1 = generateUser('BruceWayne',10);
console.log(user1); // { name: 'BruceWayne', score: 10 }
user1.increment();
console.log(user1); // { name: 'BruceWayne', score: 11 }

Enter fullscreen mode Exit fullscreen mode

Ohkay! , Bunch of stuff there..

We have now modified the generateUser() to create a new Object using Object.create() method instead of {}, using which we can now achieve classical inheritance.
More information here do check it out.

So, Object.create(userFunctionsStore) means, any and all functions declared inside of userFunctionsStore will be accessible by all newUser Objects. this is possible because the functions inside userFunctionsStore are present in the Object instance of Object.create in the prototype property which is present in the global memory space, which is referred by any new newUser Objects using a link in _proto_ property implicitly.

Using this property explicitly is deprecated , more info here

Now, the code is looking little better and also more maintainable.

But , there still is a bunch of code that we can avoid, As currently we are creating the prototype bond using Object.create() using a userFunctionsStore Object , but we can automate all of that using a keyword new as below :

function generateUser(name,score){
  this.name = name;
  this.score = score;
}

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

let user1 = new generateUser('Bruce Wayne',10);
console.log(user1); // { name: 'Bruce Wayne', score: 10 }
user1.increment();
console.log(user1); // { name: 'Bruce Wayne', score: 11 }

Enter fullscreen mode Exit fullscreen mode

We are able to add functions to the prototype of the generateUser function explicitly and also we need not create , call , return the object from generateUser. And All user Object will be able to access the prototype functions by utilizing prototypal inheritance.

It's amazing how much stuff the new keyword does for us. Read more about it here

The code now seems perfect. But there are still some changes we can do to make the code more elegant, Since currently to call generateUser() the new keyword is needed, without which the this keyword would point to Global this.
To solve this we can use a new syntax called class.

Also the best practice is to capitalize the first letter of function when we need to use new keyword for calling the function , in this case :

function GenerateUser(name,score){
  this.name = name;
  this.score = score;
}

Enter fullscreen mode Exit fullscreen mode

Bonus :- class in JS

Ohkay! , Now we will try to use class to replace the function as below :

class GenerateUser {

}
Enter fullscreen mode Exit fullscreen mode

Now we need a function, which to assign the name and score , which we can do in the constructor which is called when we call the class.

class GenerateUser{
  constructor(name,score){
    this.name = name;
    this.score = score;
  }
}

let user1 = new GenerateUser('Bruce Wayne' , 10);
console.log(user1); //{ name: 'Bruce Wayne', score: 10 }
Enter fullscreen mode Exit fullscreen mode

As simple as that, looks more clean.

But now we need to make an increment() function which we can directly declare inside a class as below :

class GenerateUser{
  constructor(name,score){
    this.name = name;
    this.score = score;
  }

  increment() {
    this.score ++;
  }
}

let user1 = new GenerateUser('Bruce Wayne' , 10);
console.log(user1); //{ name: 'Bruce Wayne', score: 10 }
user1.increment();
console.log(user1); //{ name: 'Bruce Wayne', score: 11 }
Enter fullscreen mode Exit fullscreen mode

Nothing has changed by using class instead of function, all the underlying principles are same, as we saw before using function prototypes. Just that the code is more readable and maintainable. And now you know how it works under the hood.

Thanks to Will Sentance and Kyle Simpson for their amazing work.

Let me know if you have any doubts or any issues!.

Thank you 🙏 !

Top comments (0)