Javascript Notes, Part-06 - Classes, Objects, Prototypes.
Tags: javascript, webdev, tutorial, beginners
Published: 2020-04-23 13:03:58

Let's get some terminology aside first.
* Class is a template for what things and functionalities our objects should have.
* Objects are the real existing entities that have properties and functions(methods) attached to them.
* Factory functions, they create new entities as the name suggests. Example: Object.create().
* *.* notations can be used to grab the property value of an object using a property name.

In JS objects play a very huge role!

Ok, let's dwell deep in.
```javascript
function userCreator(name, score) {
 const newUser = {};
 newUser.name = name;
 newUser.score = score;
 newUser.increment = function() {
   newUser.score++;
 };
 return newUser;
};
const user1 = userCreator("Will", 3);
const user2 = userCreator("Tim", 5);
user1.increment()
```
This is defenitely not the code you will write for your applications but there are lot of lessons to learn here, let's see what's happening.

* We have created a new factory function userCreator which creates a new object and sets properties for given arguments.
* Here we create two objects, user1, and user2 by invoking the factory function userCreator.
* Think about this, in the last line we invoke the method increment on user1. Now, look at the code of increment in userCreator. We use newUser object which is not present in our global scope, then how can we still invoke increment. That is the magic of closure again!
* Assume, we have 1000 users. For each user, we have a copy of the increment method. Is our code DRY? No.(shortcoming)
* Assume you want to add a property to an object, you manually have to do it. (shortcoming)

## Prototype chaining
Store the increment function in just one object and have the interpreter, if it
doesn't find the function on user1, look up to that object to check if it's there.

Link user1 and the object that has functions so the interpreter, on not finding .increment, makes sure to check up in that object where it would find it.

Make the link with Object.create() technique

```javascript
function userCreator (name, score) {
 const newUser = Object.create(userFunctionStore);
 newUser.name = name;
 newUser.score = score;
 return newUser;
};
const userFunctionStore = {
 increment: function(){this.score++;},
 login: function(){console.log("Logged in");}
};
const user1 = userCreator("Will", 3);
const user2 = userCreator("Tim", 5);
user1.increment();
```
Object.create(functionStore) automatically *references* methods in functionStore to our objects.
This way, we are not creating copies of the method increment.

What's going on under the hood? let's see.
* Object.create(userFunctionStore) is going to add reference value to *__proto__* property on the object(a hidden property, also read as *dunder proto dunder*)
* So, when we call user.increment(), we will first look-up if the object has the given property. If we don't find it, JS doesn't panic instead it *looks up through the prototype chain* to find the methods. This is JS's prototypal nature.
* Hence, we have removed the problem of having copies of methods for each object.
* Note the usage of *this* keyword in the above code, which generalizes the user on which we are working on. *this*(the current user) is an implicit parameter which is passed into *increment* methods.

## Inbuilt methods
So, every object has __proto__ and every object is linked to one functionStore by default which is Object.prototype which has some interesting functions like hasOwnProperty. Object.prototype is on the top of the prototype chain(its __proto__:null).

Some notes:
* Using functions inside methods will have *this* keyword set to global.
* To overcome this problem, use function.call(this) or use arrow functions which are lexically scoped.


## new keyword for cleaner code
When we call the function that returns an object with new in front we automate 2
things
1. Create a new user object
2. Return the new user object

We need to modify the factory function a little bit to 
```javascript
function userCreator(name, score){
 this.name = name;
 this.score = score;
}
userCreator.prototype.increment = function(){ this.score++; };
const user1 = new userCreator(“Eva”, 9)
user1.increment()
```
Look at how we set the object which had all the functions to userCreator function.  All functions have a default property *prototype* which is initialized to an empty object, which is where we store our methods like increment.
Now, every users __proto__ will be set to the userCreator.prototype.
* Understanding all of these concepts is pretty hard for new developers and hence classes were introduced.
* Normally, developers if not using class keyword then they would capitalize these factory functions just to let know the other developers that those factory function would need a new keyword before it.

## The class keyword
```javascript
class UserCreator {
 constructor (name, score){
 this.name = name;
 this.score = score;
 }
 increment (){ this.score++; }
 login (){ console.log("login"); }
}
const user1 = new UserCreator("Eva", 9);
user1.increment();
```

It does the same thing as the new keyword. But creates a function and object combo. function part has the variable assignments and object part has method definitions.

Let's look at the internals
first, function part, the constructor
```javascript
function userCreator(name, score){
 this.name = name;
 this.score = score;
}
```
and then, sets prototype, remaining code
```javascript
userCreator.prototype.increment = function(){ this.score++; };
userCreator.prototype.login = function(){ console.log("login"); };
```
Therefore it is readable, bundled, clean to look at and looks like other languages(but the internals are very different!).