Rethinking JS [short notes]
Tags: javascript, webdev, codenewbie, programming
Published: 2020-08-10 17:35:50

## Mental model 🧠 
- Mental models are how we think 🤔 about something
- Slow and fast thinking
- Slow thinking is laborious, front lobe
- ⚡️ Fast thinking is less tiring and most often preferred(default)
- Mental models are essential to write good code, easy to reason about and less prone to errors

---
## Context
You are on the JS asteroid in the space. You see stars⭐️, planets🪐 and asteroids ☄️ floating in space 🌌.

## Values and Expressions

### Values
- values are things. They are like numbers in Math, words in a sentence and dots in geometry. It is a thing 🧱. We can't 🚫 do much to them, but we can do things with them
- there are two types of values in JS. Primitive and Composite
- Primitive values are numbers and strings(and few more). They are like far distant stars and we can only look and refer them, but we can't change them or affect them.
- Composite values are different. We can manipulate them from code. Like functions and objects. They are like rocks closer to the asteroid that we are on.

### Expression
- expressions are kind of questions ❓ that we ask JS. The expressions always result in values. 

### `typeof`
to know the type of value we can use `typeof` operator. 
`typeof _value` will give us the type of the value as *string*.

The types can be, 
Primitive
- undefined (undefined)
- null (object)
- number (number)
- bigint
- symbol
- string
- boolean

Composite
- object (object)
- function (function)

---

## Primitives are immutable
In JS, primitives are immutable. For example

```
let name='yikes'
name[0]='l' // can't change
console.log(name) // 'yikes'
```

Even though string appears to be similar to an array, which is not a primitive we might have an intuition that we can mutate or change it. But in practice, we can't since the strings are primitive. This also applies to all the primitives.

```
let number=10
number.value='ten'
console.log(number) // 10
```

Since the addition of a property is also a kind of mutation, this too is not allowed on Primitives.

### Variables
Variables are like wires. We can connect the variables to values. In order to connect a variable wire to a value, we use assignment statements.

`let x='Shihab'`

Now the variable wire x is connected to *string value* Shihab. The RHS of an assignment is always an expression.

```
let world='World'
let say='Hello '+ world
```
Since we are asking JS, what is `'Hello '+world` it is an expression which resolves to a *value* `'Hello World'`. 

The RHS of `let x='Shihab'` is also an expression, since it also resolves to a value 'Shihab'. We call it _literlas_ since we write down the exact value. 

In JS, we always *pass the value* and not the variable itself. We cannot change what the variable points to, but at times we can change the value itself.

```
let num=10
function double(x){
   x=x*2
}
double(num) // here we pass the value 10 
            // and not the reference to it
console.log(num) // 10
```

```
let arr=[10,20]
function mutate(input){
  input[0]=30
}
mutate(arr)
console.log(arr) // [30,20]
```

This is because we pass the value of arr which is [10,20]. Since arrays are mutable, we were able to mutate the value. And the function *cannot change the value arr was wired to*, thus we get [30,20] when trying to print arr.

![image](https://img.techpowerup.org/200809/variables.png)

---

## Counting Values

We should always think, values as having a precise count.

Undefined ----> Undefined [1]
null -----> null
Boolean -----> true or false [2]
Number ----> 18 quintillion [...]
BigInit ---> Use for arbitrary precision and no round-off. Mainly used in financial calculations.
String ---> A string for each conceivable string that exists in the universe. A string has properties but it is not as same as other objects. Since the string is primitive it is immutable.
Symbols ---> recently new
Objects ---> Each time it creates a brand new Object
Function ---> Each function expressions are distinct. Like any other things in JS, functions are expressions too. When it is called with () [Call expression] JS resolves it to the return value of it. If not, it resolves to function expression or body. Function are also Objects, but special objects. Whatever you can do with Objects can be done with functions too. But what makes function different is, the can be invoked.

In this we way, we have can better place and point our variables to values. In our model, there should be only two booleans, and one `undefined` and `null`. All the time, when a primitive is being referred, JS actually *summons* them. But in the case of Objects {} and functions (), it creates a brand new value for us.

![](https://img.techpowerup.org/200809/string.png)
![](https://img.techpowerup.org/200809/obj.png)
![](https://img.techpowerup.org/200809/func.png)

---

## Equality in JS

In JS there are mainly 3 types of equalities
1. Same Value `Object.is()`
2. Strict equality `===`
3. Loose equality `==`

### Same Value
Same value returns `true` is we are pointing to the same values.

![](https://img.techpowerup.org/200809/same-value.png)

### Strict Value 
It is same as `Object.is()` expect for 

```
NaN === NaN // false
0 === -0 // true
-0 === 0
```

To test, if a number is `NaN` we can use `Number.isNaN()` or `num !== num`.

### Loose Equality
It just compares the sameness of values.

```
2=='2'
true==0
```

---

## Properties

Properties are similar to variables. They also point to values, but they start from an Object and they belong to it.

```
let sherlock={
 surname:'Homes',
 address:{
  city:'London'
 }
}
```

![Object](https://img.techpowerup.org/200810/object.png)

Even though it seems like a single object is being created there are actually two distinct objects here. An object can never reside inside another object, even though it might seem nested from code. 

```
let sherlock={
 surname:'Holmes',
 age:64
}
```

### Rules of reading a property

`console.log(sherlock.age)`

![Read property]
(https://media.giphy.com/media/Y4QVGQxzKLi6NWL4Bn/source.gif)

Properties will have names, which are strings basically. They must be unique within an object,i.e. an object cannot have two keys with the same name. The names are *case sensitive* too.

These *rules* look roughly like this:

1. Figure out the value of the part before the dot (.).

2. If that value is `null` or `undefined`, throw an error immediately.

3. Check whether a property with that name exists in our object.

   a. If it exists, answer with the value this property points to.

   b. If it doesn’t exist, answer with the `undefined` value.

If a property is missing, we get an `undefined`. But it doesn't mean that we have that property on the object pointing to `undefined`. It is more like, we are asking JS for the value (expression) and it is replying us that it is not defined, `undefined`.

### Assigning to a property

`sherlock.age=65`

1. figure out which wire is on the left side
![Read left](https://media.giphy.com/media/YNDeBFbMKydC7ZiWFV/source.gif)

2. we figure out which value is on the right side
![Read right](https://media.giphy.com/media/eid5NvMYm5yOPUF9Qz/source.gif)

3. point the wire on the left side to the value on the right side
![Point](https://media.giphy.com/media/Xev7nPYAXhMpRzbH0e/source.gif)

### Mutation

Suppose we have the following

```
let sherlock={
 surname:'Holmes',
 address:{
   city:'London'
 }
}
```

![sherlock object](https://media.giphy.com/media/kFBjlccU7RbwRzVDMg/source.gif)

```
let john={
 surname:'John',
 address: sherlock.address
}
```

![john object](https://media.giphy.com/media/J4U1l5AinL8PSzvBv0/source.gif)

Now we want to change `john`.

```
john.surname='Lennon'
john.address.city='Malibu'
```

![mutation](https://media.giphy.com/media/ftpkfq5GOxRA5HlbRN/source.gif)

But we observe we could see `sherlock.address.city` has also changed to `Malibu` from `London`. This is because both `sherlock.address` and `john.address` pointed to the same Object. 

So because of this, the mutation can be dangerous. It might unintentionally change the values at all the places where it is being referred.

In order to avoid mutation, we could have done the following:

1. When mutating `john`,

```
john={
 surname:'Lennon',
 address:{ city: 'Malibu' }
}
```


![mutation 1](https://img.techpowerup.org/200810/unnamed936.png)

2. 

```
john.surname='Lennon'
john.address={ city:'Malibu' }
```

![mutation 2](https://img.techpowerup.org/200810/unnamed-1.png)

### Is Mutation that Bad?

The mutation is not bad at all, but we should pay closer attention to it. The bliss with the mutation is, it helps us update or change a value realtime at multiple places. If think the other way, that is misery with it too.

Even though you declare an Object with `const` it will not presentation mutation to the Object. It will only prevent the reassignments. 

```
const x = {
  name:'Shihab'
}

x.name = 'Shifa' // allowed
x.age = 22 // allowed

x = {} // not allowed
```

---

## Prototype `__proto__`

```
let human={
 teeth:32
}

let gwen={
 age:19
}
```

![](https://img.techpowerup.org/200810/unnamed-2.png)

`console.log(gwen.teeth) // undefined`

But we can access `teeth` property of `human` in `gwen` by,

```
let gwen={
 __proto__: human
}
```
![giphy](https://media.giphy.com/media/MCoic0hBdqXimi1HGh/source.gif)

Now,
 
`console.log(gwen.teeth) // 32`

With adding `__proto__` we instruct JS, to _continue_ searching for `teeth` in `__proto__` too. 

### Prototype Chain

The search for the values will continue until the base `prototype` is reached. In JS the base `prototype` is `Object.__proto__` which is set to `null`.

As you can see, so this is kind of a chain that is getting created when we as JS to look for a property on an Object. This is being referred to as `prototype chain`.

```
let mammal={
 brainy:true
}

let human={
 __proto__:mammal,
 teeth:32
}

let gwen={
 __proto__:human,
 age:19
}

console.log(gwen.brainy) // true
```

![](https://img.techpowerup.org/200810/unnamed-4.png)

### Shadowing

When an Object has the same property on it and as well as inside the `__proto__`, the own _shadows_ the value on `__proto__`. This is called *Shadowing*.

![](https://img.techpowerup.org/200810/unnamed-5.png)

### Assignments

The property assignments directly happen on the Object and not on the `__proto__`. 

```
let human={
 teeth:32
}

let gwen={
 __proto__:human
}
```

![](https://img.techpowerup.org/200810/unnamed-6.png)

On `gwen.teeth=31`

![](https://img.techpowerup.org/200810/unnamed-7.png)

To check if the property belongs to an Object or its `__proto__`, we have a method called `hasOwnProperty` on Object. 

`ObjectName.hasOwnProperty(prop)`

If the `prop` is a property on `ObjectName`, it will return `true` if not `false`.

### Object prototype

When we create a new Object, there is a `__proto__` that gets added by default. It is the prototype of the Object.

![](https://img.techpowerup.org/200810/unnamed-9.png)

To terminate the prototype chain of any Object we can just assign `null` to its `__proto__`.

### Polluting prototype

All the _in-built_ methods and properties of Objects, Arrays and Strings are defined in the `__proto__` of their base. In this way, these get shared among all the values, that are being created out of it. 

![](https://img.techpowerup.org/200810/unnamed-10.png)

But this practice of sharing is highly discouraged.

![](https://img.techpowerup.org/200810/unnamed-11.png) 

But the sharing of methods and properties via the prototype chain is the base of classes and all other features. But the direct usage of *polluting prototype* is not recommended.


### __proto__ vs. prototype
You might be wondering: what in the world is the prototype property? 

The story around this is confusing. Before JavaScript added classes, it was common to write them as functions that produce objects, for example:

```
function Donut() {
  return { shape: 'round' };
}

let donut = Donut();
```

You’d want all donuts to share a prototype with some shared methods. However, manually adding `__proto__` to every object looks gross:

```
function Donut() {
  return { shape: 'round' };
}

let donutProto = {
  eat() {
    console.log('Nom nom nom');
  }
};

let donut1 = Donut();
donut1.__proto__ = donutProto;
let donut2 = Donut();
donut2.__proto__ = donutProto;

donut1.eat();
donut2.eat();
```
As a shortcut, adding `.prototype` on the function itself and adding `new` before your function calls would automatically attach the `__proto__`:

```
function Donut() {
  return { shape: 'round' };
}
Donut.prototype = {
  eat() {
    console.log('Nom nom nom');
  }
};

let donut1 = new Donut(); // __proto__: Donut.prototype
let donut2 = new Donut(); // __proto__: Donut.prototype

donut1.eat();
donut2.eat();
```

Now this pattern has mostly fallen into obscurity, but you can still see prototype property on the built-in functions and even on classes. To conclude, a function’s `prototype` specifies the `__proto__` of the objects created by calling that function with a `new` keyword.