Pass by Value, Pass by Reference & How to clone JavaScript Objects

Akshay Shegaonkar
4 min readFeb 24, 2021

First, let's look at how values are passed, copied, and stored in memory by JavaScript.

In JavaScript, there are two types of Values.

  1. Primitive Values

This is the data that has a single value and no additional methods or properties. E.g. String, Number, Boolean, Undefined, and Null. These data types are passed by value.

2. Reference Values

All Objects in JavaScript are reference types. E.g. Objects, Arrays, Classes, etc. These data types are passed by reference.

When we assign a primitive value to a variable, the actual data is stored in a memory called the “STACK” and the variable holds the address or the position of the data in the STACK.

When we assign a reference value to a variable, the actual data is stored in a memory called the “HEAP”. The memory address of this data is then copied on the STACK and the variable holds the position of this address in the STACK.

// Primitive Typeslet name = 'John';
let copyOfName = name;
name = 'Adam';console.log(name); // => 'Adam'
console.log(copyOfName) // => 'John'
// Reference Typeslet apple = {name: 'Apple', price: 120}
let copyOfApple = apple;
apple.price = 130;console.log(apple) // => {name: 'Apple', price: 130}
console.log(copyOfApple) // => {name: 'Apple', price: 130}

Notice how copyOfName doesn’t change after changing the original name variable but in the case of apple & copyOfApple, both the objects are changed. This happens because Primitive Values are passed by value and objects are passed by reference.

When we copy a variable into another, a copy of the data on the stack is created and assigned to the new variable.

So if we create a copy of a primitive type, a copy of the data which is on the STACK, in this case, the actual data is copied

But if we copy a reference type, a copy of the data which is on the stack is created, in this case, the address of the data is copied and assigned to the new variable. This explains why modifying a copied object also modifies the original object.

The same is true when passing values to a function.

function square(num) {
num = num*num;
return num;
}
function increasePriceByTen(fruit) {
fruit.price += 10;
}
let five = 5;
let squareOfFive= square(n1)
console.log(n1); // => 5
console.log(n2); // => 25
let apple = {name: 'Apple', price: 120}
let copyOfApple = apple
increasePriceByTen(apple);console.log(apple.price); => // 130
console.log(copyOfApple.price) // => 130
Image shows a graphical representation of internal memory management by Javascript. Shows primitive values are stored in the STACK and reference values are stored in the HEAP. Image shows how variables store the pointer to data stored in the stack.

Passing a value to the function square(five) copies data in variable five and stores it in the variable num . Again a copy of the return data is created and store in the variable squareOfFive. Hence, primitive types are passed by value.

Passing an object to the function increasePriceByTen(apple) copies the address of the data in variable fruit . At this point, all three variables apple , copyOfApple, fruit are pointing at the same object which is stored in the HEAP. Therefore, changing one object also changes all the other objects because they are pointing at the same object.

Now that we know what primitive values and reference values are, let’s look at how to create true copies of JavaScript Objects.

Several techniques can be employed to correctly clone an object, for starters we can use our trusty fo...in loop to iterate through all the properties of an object and copy them into another.

let fruits = ['apple', 'banana', 'orange'];
let copyOfFruits = [];
for (index in fruits) {
copyOfFruits[index] = fruits[index];
}
// for arrays we can also use the `splice` method
let anotherCopyOfFruits = fruits.splice(0);
let basket = {
capacity: 200,
contents: ['banana', 'cherry']
}
let copyOfBasket = {};
for (key in basket) {
copyOfBasket[key] = basket[key];
}

Other ways include using the Object.assign() method or use the spread operator ... .

let copyOfFruits = Object.assign([], fruits);
let copyOfBasket = Object.assign({}, basket);
let thirdCopyOfFruits = [...fruits];
let thirdCopyOfBasket = {...basket};

But all the above techniques are creating what is called Shallow Copy of the objects. What is a Shallow Copy? Let’s continue with the example code above

basket.contents.push('mango');console.log(basket.contents);
// => ['banana', 'cherry', 'mango']
console.log(copyOfBasket.contents);
// => ['banana', 'cherry', 'mango']

See the problem here? Thebasket object contains another object of type Array . The above methods will create a copy of the objects nested inside the original object. The same is true for Arrays.

let nestedArray = [ [ 1, 2, 3 ] ];
let copiedArray = Object.assign([], nestedArray);
nestedArray[0].push(4);console.log(nestedArray);
// => [ [ 1, 2, 3, 4 ] ]
console.log(copiedArray);
// => [ [ 1, 2, 3, 4 ] ]

To create Deep Copy we can use the following technique

let basket = {
capacity: 200,
contents: ['banana', 'cherry']
}
let deepCopyOfBasket = JSON.parse(JSON.stringify(basket));

Using the JSON.parse and JSON.stringify methods we can easily create Deep Copies of Objects.

We can also use the methods exposed by the Lodash library like the _.clone() &_.deepClone() to create Deep Clones of JavaScript Objects.

--

--