Deep Tech Point
first stop in your tech adventure

Pass by value vs pass by reference in JavaScript: What is the difference?

July 9, 2021 | Javascript

This article is going to discuss the difference between passing by value vs passing by reference in JavaScript. We will look at the concept of primitive values and objects which are closely connected to the concept of passing value vs reference. How is that? Well, this almost answers the question from the headline. The main difference between passing by value and passing by reference is that the first is closely connected to primitive values or primitives, while the second takes place when assigning objects. In theory, this sounds neat, but really, let’s take a look at the examples and the concept of passing by value vs reference will be much clearer.

Primitives and objects – what are they?

Let’s go back to the very beginning – the concept of data types. JavaScript is known to recognize 2 broad categories of data types:

Primitive data types or primitives encapsulate 6 more subcategories, which are:

Ok, so if these are the primitives, what are objects? Anything that is not a primitive value, is an object. Fair enough, right? Objects usually represent complex data types – they are structural types and include functions, arrays, and special non-data but structural types for any constructed object instance also used as data structures. The latter includes new Object, new Array, new Map, new Set, new WeakMap, new WeakSet, new Date, and almost everything made with new keyword.

All primitive values in JavaScript are passed by value

As simple as that? Yep, as simple as that.

But what happens when we pass by value? That means that every time we assign some value to a variable, we also create a copy of that value, and this happens every single time. Again, it is best to look at an example. Let’s start by defining two variables – x and y:

let x = 5; 
let y = x;

y = y + 3;

If we console.log(x), we get 5, and if we console.log(y), we get 8.

The first statement (let x = 5;) says a variable x has a value with the number 5. And then, the second statement (let y = x;) specifies another variable (y) and declares that variable with a value of variable x (5, remember?), and this is an example of passing by value in JavaScript – we created a copy of a value x (a number 5) and assigned (passed) that value to a variable y.
Later in the third line of the code (y = y + 3;) we change the variable y, but this change does not influence the value of a variable x.

What happens when we’re passing by reference?

The situation here is a bit different. When we create an object, we’re supplied with a reference to that object. So, if two variables hold the same values that are references to objects, in case we change the object, that change would reflect in both variables. This reflection of a change in both variables is quite different compared to passing by value when changing one variable does not affect the other.

Do you remember the article about JavaScript clone object? We talked about shallow and deep copies there, and we could apply that knowledge here, too. One important thing, though – with the shallow and deep copy we are talking about object copying, whereas with pass by value versus pass by reference we are talking about the passing of variables. Nevertheless, let’s take a look at how we can compare these two concepts. With pass by value, we are talking about a deep copy – the value of the newly created variable is not connected to the original variable and you will be able to change the value in either the original object or the cloned one without affecting the other value. So, a deep clone = the value of the new variable is disconnected from the original variable. On the other hand, when dealing with pass by reference we’re talking about a shallow clone or copy, which means that some values will still be connected to the original variable. In cases when we deal with a shallow clone you only copy the actual object. However, if the object you’re trying to copy contains nested objects, these nested objects will not get cloned.

Again, like before, it is best if we explain what happens when we pass by reference through a code example:

let a = [3];
let b = a;

b.push(5);

console.log(a); // [3, 5]
console.log(b); // [3, 5]

In the first statement (let a = [3];) we created an array, and we also defined a variable a, and we initialized the variable with a reference to the created array. So far, so good.

Then we said in the second line that let b = a; which means we defined a variable b, and we initialized variable b with the reference stored in variable a, and this is called passing by reference.

What happened in the third line of our code? b.push(5) changed our array by pushing an item 5, and because both a and b variables referenced the same array, we saw through the console.log that the mutation reflected in both a and b variables.

What if we want to compare values and references?

If we want to compare objects, it is important that we understand the difference between values and references. Why, you may ask. Well, if we’re using the strict comparison operator (triple equals ===), then two variables are equal only if they have the same value. Let’s take a look through an example:

let ten = 10; 
let tenCopy = 10; 

console.log(ten === tenCopy); // true
console.log(ten === 10); // true
console.log(ten === ten); // true

ten and tenCopy have the same value, and that is 10. The strict comparison operator (===) checks to see if the statement is true or false – in this case true as long as the value is 10 – and doesn’t really care where the value is taken from.

Ok, this was the case when we compared two values – but how does the strict comparison operator work when we compare references?
Two references are equal only if they reference exactly the same object. Let’s take a look at an example below:

// array1 and array2 hold references to different array instances
let array1 = [1];
let array2 = [1];

console.log(array1 === array2); // false

/* Why console.log(array1 === array2) results in false? 
array1 and array2 reference arrays of the same structure, 
however array1 === array2 evaluates to false because 
array1 and array2 reference different array objects. */ 

console.log(array1 === [1]);  // false

let array9 = array1;
console.log(array1 === array9); // true
console.log(array1 === array1);  // true

/* And this is the example when the strict comparison operator 
returns true - this happens only when we compare references pointing 
to the same object, in this case: array1 === array9 or array1 === array1. */

In conclusion – a difference between passing a value vs passing by reference

In this article, we discussed the difference between passing a value vs passing by reference in JavaScript. We learned that primitive data types are passed as values, which means that each time we assign a value to a variable, JavaScript creates a copy of that value.
On the other hand, objects are passed as references and if we change an object, that change reflects in variables that are referenced to that object.

In the end, we played a bit with a strict comparison operator and we learned that it is different when we compare values and when we compare references. We learned that the two variables are equal if they only have the same values, but the two variables that hold references are equal only if they reference exactly the same object.