Pass by value or reference?

A simple answer to this question is that the function arguments in Javascript are passed by value. However, when we pass an object to a Javascript function, things get a little confusing.

To understand how the objects are passed, we need to first understand how the variables store the primitive values and objects.

The objects in Javascript are stored “by reference”. When you assign an object to a variable, the variable will store the reference and not the object itself. Hence, when you pass an object variable to a function as an argument, the variable is passed by value but, in this case, the value will be the reference of the object.

Whereas, in the primitives like numbers, strings, booleans, bigint etc the variable will contain the value and not the reference.

Let us look at some examples for more clarification.

primitives

function square(b) {
    b = b * b;
    return b;
};

let a = 2;

console.log(a); // 2
console.log(square(a)); // 4
console.log(a); // 2 --> Notice the value of a remains unchanged

In the above example, we pass the variable a to function square(). The square() function modifies the value of the argument and returns the value. Since a is passed by value, the function does not modify the value of a.

In the next example, we will make use of strings.

function toUpcase(b) {
    b = b.toUpperCase();
    return b;
};

let a = "hello world";

console.log(a); // hello world
console.log(toUpcase(a)); // HELLO WORLD
console.log(a); // hello world --> the value of a remains unchanged

Since the string is a primitive type it is passed by value and the original value of a remains unchanged.

Objects

A variable when assigned an object will store the reference of the object. When you pass this variable as an argument to a function, even though we are passing the value of the variable, this value is different because it contains a reference to the object.

Hence, any changes you make in the function (wrt the passed object) will reflect in the original object.

function changeName (obj) {
    obj.fName = 'No Name';
};

let person = {
    fName: "Mickey"
};

console.log(person); // {fName: 'Mickey'}

changeName(person); 

console.log(person); // {fName: 'No Name'}

In this example, we have an object person with property fName. The person object is passed to the changeName() function. The value of fName initially is Mickey but once we call the changeName() function it changes the fName to No Name. This is because the object person is passed by reference.

Arrays

Arrays are objects in Javascript. When you assign an array to a variable, the variable stores the reference to the array. Hence, when you pass an array to a function, the argument of the function will hold the value of the reference of the array.

function addElement(arr) {
    arr.push(10);
};

let numbers = [1, 2, 3, 4, 5];

console.log(numbers); // [1, 2, 3, 4, 5]
addElement(numbers);
console.log(numbers); // [1, 2, 3, 4, 5, 10]

As you can see the original numbers array gets modified after the addElement() function is called.

Make objects immutable

We can object immutable using the Object.freeze() method. However, most of the time we want to perform operations on objects without modifying the original content.

There are several ways of doing this. In ES6, we can make use of the spread(…) operator to create a new array and avoid mutating the original array.

Note: ⚠️ 👉 The spread method or other methods like slice(), concat(), Object.assign() etc create shallow copies. If you have a complex object consider deep cloning.

function changeName (obj) {
    let newObj = {...obj};
    newObj.fName = 'No Name';
    return newObj;
};

let person = {
    fName: "Mickey"
};

console.log(person); // {fName: 'Mickey'}

console.log(changeName(person)); // {fName: 'No Name'}

console.log(person); // {fName: 'Mickey'} 

For array,

function addElement(arr) {
    let newArray = [...arr];
    newArray.push(10);
    return newArray;
};

let numbers = [1, 2, 3, 4, 5];

console.log(numbers); // [1, 2, 3, 4, 5]
console.log(addElement(numbers)); // [1, 2, 3, 4, 5, 10]
console.log(numbers); // [1, 2, 3, 4, 5]

If you do want to use the spread operator, make use of any method that creates a new array for example you can make use of the slice() method.

function addElement(arr) {
    let newArray = arr.slice();
    newArray.push(10);
    return newArray;
};

let numbers = [1, 2, 3, 4, 5];

console.log(numbers); // [1, 2, 3, 4, 5]
console.log(addElement(numbers)); // [1, 2, 3, 4, 5, 10]
console.log(numbers); // [1, 2, 3, 4, 5]