Exploring JavaScript Pass-By-Value: A look into Primitives vs. Objects

RMAG news

Spot the difference between these two samples of code and how things change.

// SAMPLE A
let a = 2;
let b = a;

console.log(a->, a, b->, b); // a-> 2 b-> 2

b= b*2;
console.log(a->, a, b->, b); // a-> 2 b-> 4

and then take a look at this

// SAMPLE B
let a = {name: Nelson M, age: 65 } ;
let b = a;
console.log(a->, a, b->, b);

// it affects the object that both a and b point to. This is because they both reference the same object in memory.
b.name = Margaret Thatcher;
console.log(a->, a, b->, b);

They both end with changing variable b but look closely at how changing variable b affects a in both scenarios.
Do you see how variable a remains the same in Sample A but changes in Sample B even though we are seemingly doing the same thing in both scenarios? What is the reason for this discrepancy?

This is how data manipulation occurs in JavaScript. Changing a primitive and non-primitive data type in JavaScript happens differently.

What are primitives, you ask? According to MDN, “In JavaScript, a primitive (primitive value, primitive data type) is data that is not an object and has no methods or properties. There are seven primitive data types”.
The primitive/non-primitive divide is the following in JavaScript

Type
Examples

Primitive
string, number, bigint, boolean, undefined, symbol, null

Non-primitive
Array, Object

When you assign a primitive, it is assigned by value. Non-primitives are passed by reference when being assigned. By the way, “reference value” is the correct term, but you will often hear just reference, and I will use those two interchangeably. Passed by reference value means that the thing you want to assign has a memory address, and the variable points to that memory address. So, that means, in essence, that if you have multiple variables pointing to the same memory location and you make a modification, it changes for all those variables that are “referring” to it – pun not intended. That is akin to having a shared collaborative Google doc – if I make a change, you see the change and vice versa. The doc I work on does not have a life of its own, independent from yours, unless you decide to copy/duplicate it. Copying or duplicating the Google doc into its own independent entity is what happens when you pass by value.

Let’s revisit the examples we’ve discussed to reinforce our understanding of variable manipulation in JavaScript.

// SAMPLE A
let a = 2;
let b = a;

console.log(a->, a, b->, b); // a-> 2 b-> 2

b= b*2;
console.log(a->, a, b->, b); // a-> 2 b-> 4

Sample A
The value 2 is assigned to the variable a. The same value that went to a is assigned to b, but it has its own memory address and is entirely independent from that of a.

// SAMPLE B
let a = {name: Nelson M, age: 65 } ;
let b = a;
console.log(a->, a, b->, b);

// it affects the object that both a and b point to. This is because they both reference the same object in memory.
b.name = Margaret Thatcher;
console.log(a->, a, b->, b);

Sample B
In non-primitives, a and b both point to the same thing. So when you change a property of b, it just changes the property of whatever in memory that b is pointing at. This happens to be the same thing that a is pointing at. Therefore, when you peek inside a, it will be identical to b because they really are the same thing.

So now let’s make a little prediction –
So far, we know up to the part I wrote above. Let’s see if we know enough to make a prediction based on what we have read. When you modify a primitive, e.g. number 2, it automatically is a reassignment to another thing. Because you are saying that the entirety of this 2 should change, unlike in the case of {name: “Nelson Mandela”, age: 65 }, which has parts/properties that you can change.  The 2 is the one and only value in and of itself, while the non-primitive value has changeable parts. So, in essence, when you do b = 3, it is a reassignment of b to a whole new thing.

So, this then begs the question of how it works when I want to reassign non-primitive b to a whole new thing. The prediction is that the non-primitive would behave similarly to a primitive reassignment if you were to change the full value. As in, if you were to do b = {name: “Margaret Thatcher”, age: 55 }, the a would remain the original and b would have its own new value.

Let’s put that to the test

// non-primitive
let a = {name: Nelson M, age: 65 } ;
let b = a;
console.log(a->, a, b->, b);
// a-> { name: ‘Nelson M’, age: 65 } b-> { name: ‘Nelson M’, age: 65 }

// it affects the object that both a and b point to. This is because they both reference the same object in memory.
b.name = Margaret Thatcher;
console.log(a->, a, b->, b);
// a-> { name: ‘Margaret Thatcher’, age: 65 } b-> { name: ‘Margaret Thatcher’, age: 65 }

// you are reassigning b to a completely new object. Doesn’t affect a because a is still pointing to the first object
b= {name: Margaret T, age: 55 };
console.log(a->, a, b->, b);
// a-> { name: ‘Margaret Thatcher’, age: 65 } b-> { name: ‘Margaret T’, age: 55 }

The prediction checks out. So, in the case of reassignment, things work the same for both primitives and non-primitives; a new value is given. The variable is assigned a new value – unless you are assigning another variable e.g. c to b, in which case the same passing by reference value principle as mentioned above applies.

Let’s go one step further. What happens when b = a but then later, b = c? Does changing c or b in this case affect a?

// non-primitive
let a = {name: Nelson M, age: 65 } ;
let b = a;
console.log(a->, a, b->, b);
// a-> { name: ‘Nelson M’, age: 65 } b-> { name: ‘Nelson M’, age: 65 }

// it affects the object that both a and b point to. This is because they both reference the same object in memory.
b.name = Margaret Thatcher;
console.log(a->, a, b->, b);
// a-> { name: ‘Margaret Thatcher’, age: 65 } b-> { name: ‘Margaret Thatcher’, age: 65 }

let c = {name: Donald Duck, age: 31 };

b=c;

// c is passed by reference value to become what b is pointing to. a remains intact
console.log(a->, a, b->, b,c->, c);
// a-> { name: ‘Margaret Thatcher’, age: 65 } b-> { name: ‘Donald Duck’, age: 31 } c-> { name: ‘Donald Duck’, age: 31 }

//modify both b and c which should both be the same thing and thus should contain both changes
b.name= b.name+1
c.name= c.name+1
console.log(a->, a, b->, b,c->, c);
// a-> { name: ‘Margaret Thatcher’, age: 65 } b-> { name: ‘Donald Duck11’, age: 31 } c-> { name: ‘Donald Duck11’, age: 31 }

As you can see in the snippet above, the answer is no because the memory address that b is pointing to, has been updated to be the address of what c is pointing to. So when b or c are both modified, it modifies the same thing but a remains intact.

We have looked at how variable assignment is not exactly uniform for all data types in JavaScript. The concpts of primitive and non primitive data types as well as passing by referenc or by value have also been introduced.

Take a look at this snippet from a stack overflow answer and let me know in the comments what you think would happen to a, b and c. Take a guess before you actually open the Stack overflow link for the answer and explanation.

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = changed;
  c = {item: changed};
}

var num = 10;
var obj1 = {item: unchanged};
var obj2 = {item: unchanged};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);
console.log(obj2.item);

LinkedIn Github

PS: always show up to your flight’s gate at least 40 mins bfore the flight departure time. I just missed one. I am writing this with a lighter wallet while stranded in the airport for 24 hours for the next available flight.