Say we're building a quiz game with users. Using objects would be the best way to store this data and functionality, as ==objects store functions with their associated data==.
Objects can be created in one of the following three ways:
#### Using object literal notation:
```js
const user1 = {
name: "Will",
score: 3,
increment: function() {
user1.score++;
}
};
user1.increment(); //➞ user1.score => 4
```
#### Using dot notation:
```js
const user2 = {}; // create an empty object
// assign properties to it
user2.name = "Tim";
user2.score = 6;
user2.increment = function() {
user2.score++;
}
```
#### Using [[Object.create]]
```js
// note: object.create() always returns an empty object
const user3 = Object.create(null);
user3.name = "Eva";
user3.score = 9;
user3.increment = function() {
user3.score++;
};
```
^8485b8
Creating the three users using the methods above, however, involves a lot of repetition, thereby making the code less DRY.
That might not be a big problem with only a handful of users, but what if there were thousands or millions of users?
### Solution 1 - Generate objects using a ([factory](https://www.geeksforgeeks.org/what-are-factory-functions-in-javascript/)) function
```js
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 user1 = userCreator("Tim", 5);
user1.increment();
```
##### Problems:
While each user's data might be different, the functionality is the same. So, every time a new user is created the function takes up space in memory--but the functions are all copies. Is there a way to write the function just once and let all the users share it?
### Solution 2 - Use the prototype chain (Prototypal Inheritance)
Store the functionality in just one object and have the interpreter, if it doesn't find the function on the current user, look up to that object to check if it's there, making the link with the [[Creating Objects#^8485b8|Object.create()]] technique:
```js
// Link user1 and userFunctionStore so that when the interpreter doesn't find increment() on user1, it checks userFunctionStore.
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);
```
##### `__Proto__`
When you create an object using `object.create()` and pass in another object as an argument, the resulting object is an empty object, but there is a link/bond between that returned empty object and the object you pass in as an argument.
The hidden/private property on the returned empty object is called `__proto__` or `[[Prototype]]`, and it points/refers to the object passed into `object.create()`.
Now, `user1` has a reference or link to `userFunctionStore`, so it can access properties on `userFunctionStore` if it needs to.
This is called [[Prototypal Inheritance]].
Running `console.log(user1)` shows that `user1`'s `__proto__` / `[[Prototype]]` property is the `userFunctionStore` object:
```js
console.log(user1);
/*
{
name: 'Will',
score: 3,
__proto__: { increment: ƒ increment(), login: ƒ login() }
}
*/
```
Every time you invoke a function, `this` is an implict parameter that is bound to the context in which the function was executed.
Such is the case for all functions, but for this discussion (i.e., object methods), it looks to the left of the of the dot:
```js
const userFunctionStore = {
increment: function(){this.score++;},
login: function() {console.log("Logged in.");}
}
const user1 = userCreator("Will", 3);
user1.increment(); // here, the js interpreter asks what context am i in? in order to know what to assign the value of `this` to
// so instead of this.score++, it becomes user1.score++
// i.e., whatever object is invoking me that's the score to increment
```
### Solution 3 - Using the `new` keyword (Pseudo-classical Inheritance)
When we call the constructor function with `new` in front, we automate two things. This methodology:
1. Creates a new `user` object
2. Returns the new `user`
```js
const user1 = new userCreator("Eva", 9);
const user1 = new userCreator("Tim", 5);
```
But now we need to adjust how we write the body of `userCreator` so that we have a way to:
- Refer to the auto-created object
- Know where to put out single copies of functions
![[new-keyword-example.jpg]]
Since the `new` keyword creates a new `user` object and returns it, we no longer need to use `Object.create()`; the `new` keyword abstracts away what `Object.create()` does for us.
In JavaScript functions are just specific types objects, but objects nonetheless.
A result of that is that we can add properties to it.
```js
function multiplyBy2(num) {
return num * 2;
}
multiplyBy2.stored = 5
multiplyBy2(3) //➞ 6
multiplyBy2.stored //➞ 5
multiplyBy2.prototype //➞ {}
```
Concomitantly, all functions have a default property on their object version called `prototype`, which is itself an empty object.
We can use treat that `prototype` property (again, it's an empty object) as our `functionStore`, and thereby replace the `functionStore` object:
```js
// Uppercase the first letter so we know it requires `new` to work
function UserCreator(name, score) {
this.name = name;
this.score = score;
}
UserCreator.prototype.increment = function() {
this.score++;
}
UserCreator.prototype.login = function() {
console.log("Logged in");
}
const user1 = new UserCreator("Eva", 9);
user1.increment();
```
___
**Tags**: #oop