Shallow copy vs Deep copy in JavaScript

Shallow copy vs Deep copy in JavaScript

As we differentiate between variables in JavaScript as primitive and non-primitive, similarly when we copy those variables we get a deep copy and a shallow copy respectively. In general, when you try to copy an existing thing, we expect that the original thing stays intact. But however, we often get unexpected results. This happens when we don't understand the difference between deep and shallow copy and what actually happens when copying those variables.

First of all, let's differentiate between deep copy and shallow copy. Deep Copying will not have any relation with the variable it was copied from. It gets detached completely. However, when shallow copying, the original variable also gets affected when the new variable is modified.

Examples

➡️ Copying primitive data types

var a = 10;
var b = a;
b = 20;
console.log(a); // 10
console.log(b); // 20
let a = true;
let b = a;
b = false;
console.log(a); // true
console.log(b); // false

This makes the copy of the old variable and allocates a separate memory location to it.

But, this is not the case with non-primitive data types i.e. Objects and Arrays. When they are created, they are stored for only once in the memory. By copying them, it creates a reference to that original variable.

Let's create a shallow copy.

const a = {
  name: "Angel",
  message: "You will do good"
}
const b = a;
b.message = "You are amazing";

console.log(a.message); // You are amazing
console.log(b.message); // You are amazing

➡️ But wait, why is this happening ? Because, arrays and objects when copied create just a reference to it's original variable. So whenever the updating happens, it will update the reference that it has been stored into.

In order to access the original data in this case, we need to copy the object differently. For copying composite data types, follow as shown.

➡️ For example,

// method 1: ES6 spread operator 
const a = {
  color: "black",
  num: 4,
}

let b = {...a} // spread operator 

b.num = 1;

console.log(a.num) // 4
console.log(b.num) // 1

// method 2: Object.assign
const a = {
  color: "black",
  num: 4,
}

let b = Object.assign({}, a) // spread operator 

b.num = 1;

console.log(a.num) // 4
console.log(b.num) // 1

➡️ Similarly, for arrays.

// method 1: ES6 spread operator 
let fruits = ["Apple", "Mango", "Peaches"];
let fruitsCopy = [...fruits];

fruitsCopy[2] = "Orange";

console.log(fruits); // ['Apple', 'Mango', 'Peaches']
console.log(fruitsCopy); // ['Apple', 'Mango', 'Orange'];

fruits[1] = "Banana";
console.log(fruitsCopy); // ['Apple', 'Mango', 'Peaches']

// method 2: map/ filter/ reduce -> returns a new array 

let fruits = ["Apple", "Mango", "Peaches"];
let fruitsCopy = fruits.map(item => item);

fruits[1] = "Banana";
console.log(fruitsCopy); // ['Apple', 'Mango', 'Peaches']

// method 3: slice -> returns a new array 

let fruits = ["Apple", "Mango", "Peaches"];
let fruitsCopy = fruits.slice();

fruits[1] = "Banana";
console.log(fruitsCopy); // ['Apple', 'Mango', 'Peaches']

However, this does not work when working with nested values. For example, using the above methods will result in creating the same reference copies for the nested values. Hence, deep copying comes to the picture.

➡️ Deep copy

As read above, deep copy doesn't have any relation with which it was copied, because now the reference to the original and the copied object is different.

let myBook = {
author: 
    {
      lastname: "Doe",
      firstname: "Jane"
    },
title: "JavaScript Concepts",
category: ["Non-Fiction", "Technology"]
 }

// method 1: using json methods

let myBookCopy = JSON.parse(JSON.stringify(myBook));

// changing values
myBookCopy.title = "JavaScript"
myBookCopy.category[1] = "Tech"
myBookCopy.author.firstname = "John"

console.log(myBook) 
/* {
author: 
    {
      lastname: "Doe",
      firstname: "Jane"
    },
title: "JavaScript Concepts",
category: ["Non-Fiction", "Technology"]
 }
*/
console.log(myBookCopy)
/* 
{
author: 
    {
      lastname: "Doe",
      firstname: "John"
    },
title: "JavaScript",
category: ["Non-Fiction", "Tech"]
 }
*/

// method 2: using recursion 

const getDeepCopy = values => values.map(item => Array.isArray(item) ? getDeepCopy(item) : item);

let arr = [1, [2, 3], 3];
let arrDeepCopy = getDeepCopy(arr)

// changing values
arrDeepCopy[0] = '*'; 
arrDeepCopy[1][1] = '*'; 

console.log(arrDeepCopy); // [ '*', [ 2, '*' ], 3 ]
console.log(arr); //  [1, [ 2, 3 ], 3 ]

Thats, it ! If you enjoyed reading, do consider sharing so that it reaches to more people ! 👍❤️