How JavaScript works behind the scene- Primitive vrs Reference Values

How JavaScript works behind the scene- Primitive vrs Reference Values

Hello World, Akwaaba ๐Ÿ‘‹. JavaScript allows us to work with two data types: primitive and non-primitive. When you create a variable in JavaScript, it can store two types of values, a primitive value or a reference value. At the end of this article, you should be able to understand how these two data types work behind the scenes. Let's get started ๐Ÿ‘Š.

Primitive vrs Reference Values

What are Primitive data type

There are seven primitive data types: string, number, bigint, symbol, boolean, null, and undefined.

Primitive data types can store a single value.

All primitives are immutable meaning the values are incapable of being changed. The variable can be reassigned to a new value, but the actual value can not be changed.
To check if a value is a primitive value, you can use the typeof next to the variable name, this will return any of the data types listed above.

Let's take a look

var name = "Emmanuel";
typeof name ; /* returns  "string" */
var myNumber = 45; 
typeof myNumber ;  /* returns  "number" */

var result = true; 
typeof result;  /* returns "boolean" */

What are non primitive data types

If you want to store multiple or complex values then you need to use non-primitive data types. Object literals, Arrays, and functions are basically objects or what we will call non-primitive values.

Non-primitive values do not have a fixed size. For instance Objects, for example, can be of any length, arrays can have any number of elements and a function can contain any amount of code in its block.

Because these types do not have a fixed size, their values will not be stored directly in the memory associated with each variable. Instead, the variable stores a reference to the value.
Confusing ๐Ÿ˜”, we dive deeper as we move along ๐Ÿšถ

Let's now take look at what happens in the JavaScript engine when it encounters these two data types.

Inside the JS Engine

The JavaScript engine is made of two parts, the call stack and the heap. All primitive values are stored in the call stack since the size of a primitive value is fixed. However, reference types or non-primitive data types are stored in memory in the heap because the size of a non-primitive or reference value is dynamic.

Now let's take a look at what happens when you assign either primitive or non-primitive value to a variable ๐Ÿ˜Ž

Assigning values

When you want to assign a value to a variable, the JS engine will check to see if the value is primitive or non-primitive value. If the value is a primitive value it will be stored in the call stack because it does not take so much memory.

Let's now investigate what happens to primitive values under the hood. ๐Ÿ˜Ž

Understanding passed by value and passed by reference.

Primitive data types are passed by value whilst non-primitive data types are passed by reference.

To understand passed by value and passed by reference, we need to understand what happens when we create a variable and assign a value to it.

How primitive values work in the JS Engine

Let's use the code below and see what is happening under the hood

var x = 2;
  • In the above when we declare a variable and assign it a value, the JS engine creates aunique identifier then the assignment operator (=) will allocate some space in the memory of the call stack and store the value 2.

  • It then returns the location or address of the allocated memory space. Eg Ax001

  • The variable x is therefore pointing to the location or address of the allocated memory space (Ax001) and not pointing directly to the actual value of 2.

  • As a result, if you want to access the value, you will need to go to the location or address in memory i.e Ax001

Copying primitive values

Let's now look at what happens when you copy a value to another variable.

Let's use the example below

var x = 2; 
var y = x;
  • When you copy the value stored in x to y, you are basically copying the allocated address in memory (i.e Ax001) to the variable y.

  • The variable y and the variable x all point to the same address in memory

  • The variable y does not really know about the value stored in x it only knows of the location or address to go to find the value of x.

What is happening in the above

When you assign a variable that stores a primitive value to another variable, both the former and latter variables point to the same address in memory. So, if the address in memory for the x is Ax001 then the address in memory for the y will be Ax001.

var x = Ax001 /* x is pointing to the allocated address of the value 2  not the value directly */
var y = x; 
var y = Ax001 /*y is pointing to the same allocated address in memory */

Modify or changing the value in a variable

Let's now assume you want to modify or change the value stored in x, will the value at y change too? Let's find out

x = 20;
console.log(y) ; /* 2*/
console.log(x); /* 20 */
  • Changing the value of x to 20 does not change the value of the variable at y, why?

  • Because primitive values cannot be changed, the JS allocates a new address in memory (Eg. Ax002) and stores the new value there.

  • So whiles x now points to the allocated address of Ax002, the variable y still points to the previously allocated address in memory (ie Ax001).

  • Hence if you change the value stored in x to 20 it will not affect the value stored in y. ๐Ÿ˜ฑ

So what is the verdict?

We can conclude that primitive data types when they are passed to another variable, are passed by value.

What it simply means is, instead of just assigning the value to same address or location in memory, the value will be passed to a new location or allocated space in memory.

Reference values

Objects are stored in the heap of the JS Engine, the reason being, Objects have complex values which do not have fixed sizes hence it is appropriate to store them in a place with big memory.

When we create a variable and assign a non-primitive data type to it, the JS engine creates a unique identifier, allocates an address in memory on the call stack, but stores the actual value in the heap. Now because we declare the variable in the 'call stack', it will allocate some address in memory and then store the value. But wait, the value was actually stored in the heap. So what will happen is, we will have to forward the allocated location or address to the value stored in the heap to the call stack first.

So the variable declared in the call stack will point to the forwarded address or reference address in the call stack and from the call stack we will point to the actual value stored in the heap.

Let's break it down with some code

var user = { name: "Emmanuel", age: 34} ;
var user = # 1234; /*the object is stored in the address #1234 in the heap */
/* When the variable is declared,  we will create a space in memory (location ) and store the value 
But the value was stored in the heap, so the address or location of the heap will be passed to the call stack.
Meaning the user variable will point to the reference address or location  
*/

Copying values to Object

Let's see what happens when we copy an object to another object

var user = {name: "Emmanuel", age: 34}
/*user points to the address #1234 in the heap */
var user2 = user  /*Assign value of user to user2 */

Passing a reference value

When you assign a reference value from one variable to another, the value stored in the variable will also be copied into the location of the new variable. As a result, the values stored in both variables all point to the address or location of the actual object stored on the heap.

So, both the user and user2 variables, are referencing the same object

  • With the code above, the assignment operator, will straight ahead pass the location of the variable user which is #1234 to the variable user1.

  • In other words, the reference of the variable user is passed to the variable user1

  • user1 points to the same address as user ( user1 points to #1234 as well ).

Changing or modify the value

Finally, let's take a look at what happens when you modify or change the value stored in the initial variable, user. In the code below, we are adding another property country to user

var user = {name: "Emmanuel", age: 34};
var user1 = user;
user.country = "Ghana"; /* Assign a value to user object  */
console.log(`Details of user`, user);
/* Details of user {name: "Emmanuel", age: 34, country: "Ghana"}*/
console.log(`Details of user1`, user1);
/* Details of user1 {name: "Emmanuel", age: 34, country: "Ghana"} */
What is happening above?
  • Changing or modifying the values in the user object, modifies the value in the user1 object.

  • This is happening because both user and user1 all point to the same object or they reference the same object in the heap.

  • Any property that is modified in one variable, will change or modify the value in the other variable.

From the above example, we can conclude that when passing non-primitive data types, the assignment operator directly passes the address or reference. Hence, non-primitive data types are always passed by reference.

What this means is, whenever you are copying an object, you are pointing to the exact same value in the heap. Knowing this will avoid so many headaches when working on Object or non-primitive data types in JavaScript.

#Summary In this article, we learned about the primitive values and non-primitive values.

  • There are seven primitive types: string, number, bigint, symbol, null, and undefined. Primitive data types can store a single value. Primitive data types are passed by value.

  • Object, Arrays and Functions are non-primitive data types. Non- primitive data types are passed by reference, hence they are also known as reference values.

If you found value in this post, kindly share it on your social media channels. It will be a great reference to a code newbie. Did you find value in my post, I would love to read your comment on it. Me daa se

PS: Akwaaba and Me daa se are Ghanaian ๐Ÿ‡ฌ๐Ÿ‡ญ dialect meaning, Welcome and Thank you respectively

Writing with โค๏ธ from ๐Ÿ‡ฌ๐Ÿ‡ญ