Simple Explanation of Objects and Memory References in JavaScript

What will be the result of the above code?

Before I say the answer, let’s look at some concepts in JS and programming general.

In JavaScript, we have two value types:

  • Primitive
  • Object

Primitive values are string, boolean, numbers, null, undefined.

Objects are literals, Arrays, Boolean, Date, RegExp, Function, etc.

How we ever thought about how they are stored in memory? Primitives are held directly by their values, while Objects are held by their references.

What does it mean? During the execution of a program, the program is allocated two spaces: Heap and Stack. Stack stores variables and function arguments here in an orderly fashion First-In-Last-Out. Heap is a memory area where variables are stored in an unorderly fashion.

When we create variables in JS they are created in the stack. The Primitives are allocated or held in the stack, while the Objects are allocated in the Heap and the memory address of the allocation is held in the stack:

Heap
----
| |
| |
| |
| |
Stack
memory addr
-----
000 | 2 |
001 | |
002 | |
003 | 5 |
004 | |
005 | |

We see a model of how stack and heap are created in memory. See each memory cell is referred to by a memory address. Memory address at #000 holds the value number 2. Memory address #005 holds no value. Memory address #003 holds value 5.

If we write a JS script with primitive values like this:

const num1 = 90
const num2 = 100

Their representation in RAM would be like this:

Heap
----
| |
| |
| |
| |
Stack
memory addr
-----
num2 000 |100|
001 | |
002 | |
num1 003 | 90|
004 | |
005 | |

See, they are stored on the stack. num2 is held at mem addr 000 with value 100 and num1 is held at mem addr 003 with value 90. Because they are primitives their values are held directly on the stack.

So when we want to access num2, we will be accessing its mem addr 000, the CPU will then retrieve the value 100:

const num1 = 90
const num2 = 100
num2 // 100, this would get the value held at mem addr #000

Whe we write a JavaScript script with Object values:

const obj1 = { val: 80 }
const obj2 = { val: 90 }

This will be modeled like this:

Heapmem addr ----
020 | |
021 | 80 |
022 | |
023 | 90 |
024 | |
Stack
memory addr
-----
obj2 000 | 023 |
001 | |
002 | |
obj1 003 | 021 |
004 | |
005 | |

See, the Objects are allocated on the stack but their values are allocated on the Heap! Their values on stack hold the mem addr in the heap where the values are.

The obj1 is located at mem addr 003 in the stack, the value there 021 is the mem addr in the heap where the value is stored. Looking at mem addr 021 in the heap we see it holds 80, the value of the obj1.

The same goes for obj2. Its value is located in heap at mem addr 023.

When we want to access obj1

obj1

obj1 is stored at 003. The engine will get the value stored at 003 which is 021. Then. the value of the mem addr 021 in the heap is retrieved and returned. There is much to this deep down but I kept it simple for us to get the gist.

Now, if we create a new variable obj3 and assign it to obj1:

const obj1 = { val: 80 }
const obj2 = { val: 90 }
const obj3 = obj1

A mem space will be created in the stack to hold obj3, the value stored at obj1 mem addr 003 will be copied and stored at obj3.

The value at obj1 which is a memory address pointing to a heap space will have obj3 holding the mem addr stored at 003. See the memory model.

Heapmem addr ----
020 | |
021 | 80 |
022 | |
023 | 90 |
024 | |
Stack
memory addr
-----
obj2 000 | 023 |
001 | |
002 | |
obj1 003 | 021 |
004 | |
obj3 005 | 021 |

Now, obj3 and obj1 point to the same mem addr at the heap. When we change the val property in obj1:

obj1.val = 111

The engine will look at the value stored at 003 and will see 021. It will go to the heap at mem addr 021 and change the val there to 111. The memory model:

Heapmem addr ----
020 | |
021 | 111 |
022 | |
023 | 90 |
024 | |
Stack
memory addr
-----
obj2 000 | 023 |
001 | |
002 | |
obj1 003 | 021 |
004 | |
obj3 005 | 021 |

Now, if we access the val property at obj3.

obj3.val // 111

We see that the val property would be changed too. It was affected by the val property done at obj1. This is because obj1 and obj3 point to the same mem addr.

Heapmem addr ----
020 | |
021 | 111 |
022 | |
023 | 90 |
024 | |
Stack
memory addr
-----
obj2 000 | 023 |
001 | |
002 | |
obj1 003 | 021 |
004 | |
obj3 005 | 021 |

Accessing the val property at obj3, will make the engine lookup the value stored at 005 which is mem addr where obj3 is located on the stack. The engine will see 021 at 005, then it will locate the value of the mem addr 021 at the heap, which is 111.

obj3 and obj1 refers or points to the same memory in the heap, so any modification from one will affect the other.

So this answers the question at the beginning of this post:

const person1 = { name: "Chidume" }
const person2 = person1
person1.name = "Nnamdi"
person2

Let’s take it one code at a time:

const person1 = { name: "Chidume" }

The object person1 is created and its properties are stored on the heap. The mem addr of the location at heap is held by the person1 in the stack.

Heapmem addr -----------
020 | |
021 | |
022 | |
023 | "Chidume" |
024 | |
Stack
memory addr
-----
person1 000 | 023 |
001 | |
002 | |
003 | |
004 | |
005 | |

When we create person2 and assign it to person1.

const person2 = person1

The value which is a mem addr at person1 is assigned to person2 on the stack

Heapmem addr -----------
020 | |
021 | |
022 | |
023 | "Chidume" |
024 | |
Stack
memory addr
-----
person1 000 | 023 |
001 | |
002 | |
003 | |
person2 004 | 023 |
005 | |

See, person1 points to 023 at the heap, person2 points to same 023 at the heap. They are stored in different locations on the stack but they point or refer to the same mem location on the heap.

When the “name” property is changed:

person1.name = "Nnamdi"

The engine will locate the person1 at 000 then read the value to be 023. It will refer to the mem addr 023 at the heap, and change the value to “Nnamdi”:

Heapmem addr ----------
020 | |
021 | |
022 | |
023 | "Nnamdi" |
024 | |
Stack
memory addr
-----
person1 000 | 023 |
001 | |
002 | |
003 | |
person2 004 | 023 |
005 | |

Now, when we access the person2.

person2 // { name: "Nnamdi" }

The engine will look at the mem addr 004 of person2 in the stack. It will see 023, which is a mem addr at heap. The engine will then go to the heap and retrieve the value from the mem addr 023 which is “Nnamdi”.

We see that Objects are referenced by memory address and primitives are referred to by values.

This is same in C/C++:

int* i = new int(10);
int ii = 10;
int* iii = i;

In the first statement, a space is allocated on the heap and the number 10 is written to it. Then, the mem addr of the heap space is assigned to i on the stack.

In the second statement, 10 is created on the stack and assigned to ii.

In the third statement, iii is created on the stack and assigned to hold i. The value in i won't be the value 10 but the mem addr where 10 was allocated and stored, for example, #0987654. So iii would point to the mem addr #0987654 same as i.

Now, if iii changes the value at #0987654,

*iii = 99;

the changes will be reflected in i.

cout << *i << endl; // 99

This is all due to memory references.

Conclusion

We have understood how memory pointers work in JS and in programming. What memory references are and how they are passed around in objects.

So, next time when you are creating and manipulating objects, you will know and understand how it can have a chain effect on other variables pointing to it.

If you have any question regarding this or anything I should add, correct or remove, feel free to comment, email or DM me

Thanks !!!

Thanks for stopping by my little corner of the web. I think you’ll love my email newsletter about programming advice, tutoring, tech, programming and software development. Just sign up below:

Dev Proto

News, articles, and knowledge bank for software developers