Javascript Objects Deep Dive: References, Primitives, Equality, Immutable, Tree and Circular 
Tags: immutable, object, circular, primitives
Published: 2020-06-28 06:59:17

Objects are a fundamental block for creating application state. Therefore, it's essential to understand how they work and what their manipulation strategies are.

**Note:** *This article makes assumptions based on the way objects are handled in Javascript. Programming languages differ slightly on the topic, but many of the concepts covered below can be applied to your language of choice.*

# Variables and Memory

Our programs need to store, read and possibly modify data while running. Hence, there's a need to reach previously defined values in our code for later use. With Javascript, that can be done with **variables:**

```typescript
let welcomeMessage = 'Hello World';
let age = 38;
let hungry = true;
```

Let's use a **simplified analogy** to represent what's going on under the hood. There are 2 tables of correspondence:
* First table relates **memory addresses** to their **current values**. The number of addresses depend on the size of the available RAM in the machine.

Address | Value
--- | ---
001 | 'Hello World' |
012 | true |
043 | 38 |

* Second table relates **variable names** to **memory addresses**. These are the variables declared in our program.

VarName | Address
--- | ---
welcomeMessage | 001
age | 043
hungry | 012

*💡 Memory address indexes do not necessarily have a pattern.*

## References

See those memory address codes in the table (`001`, `043`, `012`)? We will call them **References** from now on. They're the **physical address in memory** where values are **being stored**. 

*Important*: References are **NOT** variable names. Rather, variable names are pointing to a reference. That's an important distinction we have to make in order to understand the next concepts.

## Reading Some Variable

In order to read some variable (for example, `age`), the program will obtain its specific memory address (reference), and then its value:

`age` -> `043` -> `38`


# Immutability
Think about creating a payment check to someone. **Once we are done writing it, we expect it to remain unchanged.** That's the reason we use pens (no removals) and cross-out void spaces (no additions).
 
> What if the check had been misspelled?

In that case, it's a better idea to write a new check instead of overwriting the current one, shred the current check and throw it away in the garbage.

We are about to see that **Immutability is also used in many programming scenarios**, often due to the same reason (guaranteeing **consistency**).

# Primitives

> a primitive (primitive value, primitive data type) is data that is not an object and has no methods.

According to [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Glossary/Primitive), Javascript contains 7 primitives:  `string`, `number`, `bigint`, `boolean`, `undefined`, `symbol` and `null`.

We can consider Primitives one the lowest building blocks in the language. It's important to note that **they have no methods**.

> I can call `.toString()` on a number, doesn't that mean it has properties/methods?

Not really. Javascript applies [Autoboxing](https://dev.to/benjaminmock/do-you-know-what-autoboxing-in-js-is-enl) to primitives. One quick way to check this is to try creating a new property in the number:

```typescript
let myNumber = 345;
myNumber.previousNumber = 344;
myNumber.previousNumber //undefined
```

If they did have properties, `previousNumber` would have been saved in the example above.

## Primitives are Immutable

One important characteristic of primitives is that they **do not allow for modifications** after they are created.

> Primitives are written with pens.

What does Immutable mean in programming terms, though? Going back to our memory analogy:

```typescript
let myCoolPrimitive = 'This is a primitive';
```

Address | Value
--- | ---
047 | 'This is a primitive' |

VarName | Address
--- | ---
myCoolPrimitive | 047

The value inside Reference `047` is immutable. It cannot be altered anymore. So how to change the value for `myCoolPrimitive`?

```typescript
let myCoolPrimitive = 'This is a primitive';
myCoolPrimitive = 'Can I change it though?';
```

Address | Value
--- | ---
047 | 'This is a primitive' |
019 | 'Can I change it though?'

VarName | Address
--- | ---
myCoolPrimitive | 019

The reference has been updated to a new string.

Let's also use an example with 2 variables this time:

```typescript
let firstPrimitive = '1st primitive';
let secondPrimitive = firstPrimitive;
```

Address | Value
--- | ---
001 | '1st primitive' |

VarName | Address
--- | ---
firstPrimitive | 001
secondPrimitive | 001

```typescript
/* ...Continuing code from snippet above */
secondPrimitive = 'Changed to second primitive';
console.log(firstPrimitive) // '1st primitive'
```
Address | Value
--- | ---
001 | '1st primitive' |
002 | 'Changed to second primitive'

VarName | Address
--- | ---
firstPrimitive | 001
secondPrimitive | 002

**Only the targeted variable (**`secondPrimitive`**) will change its value.**

# Objects

**If some value is not a primitive, then it is an object.**

> JavaScript objects are containers for named values, called properties and methods.

Source: [W3Schools](https://www.w3schools.com/js/js_object_definition.asp)

Examples of objects include (but are not limited to): `{}`, `[]`, `Function`, `Date`, `HTMLElement`, `Set`, `Map`, etc. 

> Objects are written with pencils

Unlike primitives, **one can create an object and modify its properties later. That's mutable behavior**, let's see an example:

```typescript
let user1 = {name: 'Josh', credit: 200};
let selectedUser = user1;
let creditVar = user1.credit;
```

Address | Value
--- | --- |
001 | {name: `$002`, credit: `$003`} |
002 | 'Josh' |
003 | 200 |

💡 Object properties point to memory addresses. `$` notation represents a reference.

VarName | Address
--- | ---
user1 | 001
selectedUser | 001
creditVar | 003

```typescript
user1.credit = 600;
```

Address | Value
--- | --- |
001 | {name: `$002`, **credit:** `$004`} |
002 | 'Josh' |
003 | 200 |
004 | 600 |

VarName | Address
--- | ---
user1 | 001
selectedUser | 001
creditVar | 003

```typescript
console.log(selectedUser.credit) //600
console.log(creditVar) //200
```

* Credit Number got updated, it's a primitive so it **created a new reference** (`$004`).
* **The object was updated after it's creation** (updated credit number reference from `$003` to `$004`). Therefore, it is showing **mutable behavior**.
* `selectedUser` and `user1` are pointing to the same object. **They are sharing the object and its properties**, and if one variable updates a property, **the other variable will also reflect the changes.**
* `creditVar` never got updated to 600 because it's pointing to the 200 primitive that was previously defined.

## Objects Inside Objects

It's also possible to "nest" objects.

```typescript
let math101 = {difficulty: 0.3}
let courses = {math: math101}
```

Address | Value
--- | --- |
001 | {math: `$002`} |
002 | {difficulty: `$003`} |
003 | 0.3


VarName | Address
--- | ---
math101 | 002
courses | 001

![Nested Objects](https://dev-to-uploads.s3.amazonaws.com/i/ezygc9au6w9p7i5rlq7x.png)

## Circular Objects
It's possible to reference one object to itself as a property.

```typescript
let a = {};
a.b = a;
```

Address | Value
--- | --- |
001 | {b: `$001`} |


VarName | Address
--- | ---
a | 001

![Circular object: Property b of object refers to itself](https://dev-to-uploads.s3.amazonaws.com/i/zjwks0bhps9vp9wvwrpm.png)
It's also possible to reference 2 or more objects in a circular way.

```typescript
let o1 = {};
let o2 = {};
o1.o2 = o2;
o2.o1 = o1;
```

Address | Value
--- | --- |
100 | {o2: `$101`} |
101 | {o1: `$100`} |


VarName | Address
--- | ---
o1 | 100
o2 | 101

![Circular objects: o1 property refers to o2, while o2 property refers to o1](https://dev-to-uploads.s3.amazonaws.com/i/gy9jd89pr6ouzq96ol0z.png)

## Object Tree
An Object Tree is a specific type of object that contains child objects where:
* An object can only appear once in the tree (0 or 1 incoming edges).
* There are no cycles in the graph.

This is a **valid** example of an Object Tree

![Valid Tree](https://dev-to-uploads.s3.amazonaws.com/i/d05qtnux3ai7ixlt9mcv.png)

**Invalid** example (`E` has 2 parent objects)

![E object has 2 parents](https://dev-to-uploads.s3.amazonaws.com/i/8kzy06wacfje2jojdtod.png)

**Invalid** example (Graph contains cycle `A -> C -> E`)

![A C E cycle](https://dev-to-uploads.s3.amazonaws.com/i/3ydjvl89jh9gox6c6u1p.png)

# Garbage Collection

You may have noticed in our examples that values can stay in memory even after they're not being used by any other variables (or objects). Fortunately Javascript comes with a Garbage Collector, which **periodically wipes away unreachable values automatically**, so we don't have to worry about explicitly deleting them from memory:

```typescript
let someVar = 123;
someVar = "Now I'm a string!";
```

Address | Value
--- | --- |
001 | 123 |
002 | "Now I'm a string" |

VarName | Address
--- | ---
someVar | 002

After Garbage Collection, Address `001` should be released:

Address | Value
--- | --- |
002 | "Now I'm a string" |

VarName | Address
--- | ---
someVar | 002

Garbage Collection is dependent on the environment's implementation (Browser, Node.js, etc), but generally uses the "Mark-and-sweep Algorithm", according to [this MDN Article about Memory Management](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management)

# Equals Operator

**NOTE:** As a rule of thumb, **use Triple Equals Operator** (`===`) when possible instead of (`==`). [See why here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness)

In Javascript, the equals operator works differently for primitives and non-primitives:

```typescript
let firstString = 'The quick brown fox jumps over the lazy dog';
let secondString = 'The quick brown fox jumps over the lazy dog';
console.log(firstString === secondString) // true

let firstObject = {};
let secondObject = {};
let thirdObject = secondObject;
console.log(firstObject === secondObject) //false
console.log(secondObject === thirdObject) //true
```

Address | Value
--- | --- |
001 | 'The quick brown fox jumps over the lazy dog' |
002 | 'The quick brown fox jumps over the lazy dog' |
003 | {} |
004 | {} |


VarName | Address
--- | ---
firstString | 001
secondString | 002
firstObject | 003
secondObject | 004
thirdObject | 004

When comparing 2 variables with `===`:
* **Primitive Values** will return `true` when their content is the same (even if memory addresses are different for each variable!)

* **Objects** will return `true` **only if** the 2 variables point to the same memory address.

**Comparing 2 objects deeply** (by their content instead of reference) **is often not desired** since it can be computationally expensive and unpredictable (depends on the depth and key length of the objects). That being said, in extreme cases it's recommended to use some library method, for example [Lodash's isEqual function](https://lodash.com/docs/4.17.15#isEqual)

# Immutable Objects

## Avoiding Side Effects

Sharing the same object is a performance improvement (less memory is allocated). But **there are times when you need independent copies**. One example is extending from a base object. 
**In order to create Immutable objects, We can use spread operators for Arrays (`[...]`) and Objects (`{...}`)**

💡 **Remember:** The new object should behave as *"read-only"* after being cloned and applied the desired changes. **If subsequent changes are needed, a new immutable object should be created**. This will guarantee the **object consistency** promised by the immutable strategy.

```typescript
let baseCar = {
    color: 'black', 
    convertible: false, 
    passengers: 5
}

let fancyCar = {...baseCar, convertible: true} 
/* {
    color: 'black',
    convertible: true,
    passengers: 5
} */
```

By creating different references, we can be sure that the `baseCar` will not be altered when there is a change in `fancyCar`. **This avoids side-effects in our code.**

![Object extends another base object by using spread syntax](https://dev-to-uploads.s3.amazonaws.com/i/xmvfytdg42ql3swyi6t3.png)

## Benefits Of Immutability

By using the concept of Immutable objects we can better integrate our code with features like:

* **Faster Change Detection**: Updates often change only part of an object. By using `===` operator (fast), deep object comparison is possible by using references, avoiding expensive full object traversal (e.g. Lodash's isEqual). 
> If `newObject !== oldObject`, something changed. We can recursively inspect its properties and find the changed values faster.

* **"Time Travel"**: Previous versions of objects can be kept in memory. Each version is a **snapshot** of the object, which allows for inspection through time and helps to debug the code.


## Nested Immutable Updates

Updating a nested object Immutably is relatively simple when your Object structure is a Tree. All we have to do is use the spread syntax in all the levels preceding the property.

Let's update `obj.a.b.c` immutably to `false` in the example below:

```typescript
let obj = {
    a: {
       b: {
           c: true,
           e: 'mnop'
       },
       d: 456
    }
    y: {
        z: 12
    }
}

let obj2 = {
    ...obj,              //Copy obj properties
    a: {                 //Overwrite property obj.a
        ...obj.a,        //Copy obj.a properties 
        b: {             //Overwrite property obj.a.b
            ...obj.a.b,  //Copy obj.a.b properties
            c: false     //Overwrite obj.a.b.c property
        }
    }
}  
```
The resulting object contains all properties defined in `obj`, with the update on `obj.a.b.c = true`. Notice **only the changed objects** have been cloned, so we can **still use reference equals operator** for unchanged properties:

```typescript
obj.a === obj.a; //false
obj.y === obj2.y; //true
```
This is great for both cloning and equality check performance.

## Complex Immutable Updates and Improvements

> * Is it possible to improve copying performance of nested objects when **batching updates**?

> * How to update nested immutable objects when they are not in the shape of a tree? How do multiple parents and object cycles interfere with our workflow?

These questions are harder to answer and will be covered in the next part of our series. Thanks for reading and stay tuned for the following chapters :)