Deep Copy and Shallow Copy in Javascript.

Pawan Verma
3 min readJun 29, 2021

What is copy?

In programming, we store values in variables. Making a copy means that you initiate a new variable with the same value(s).

We have two type of Copy

  1. Deep Copy : deep copy makes a copy of all the members of the old object, allocates separate memory location for the new object and then assigns the copied members to the new object. In this way, both the objects are independent of each other and in case of any modification to either one the other is not affected.
  2. Shallow Copy : A shallow copy means that certain (sub-)values are still connected to the original variable.

Problem In Shallow Copy : a reference variable mainly stores the address of the object it refers to. When a new reference variable is assigned the value of the old reference variable, the address stored in the old reference variable is copied into the new one. This means both the old and new reference variable point to the same object in memory. As a result if the state of the object changes through any of the reference variables it is reflected for both

We have two categories of data types in javascript

Primitive data types

Primitive data types include the following:

  • Number — e.g. 1
  • String — e.g. 'Hello'
  • Boolean — e.g. true
  • undefined
  • null

When you create these values, they are tightly coupled with the variable they are assigned to. They only exist once. That means you do not really have to worry about copying primitive data types in JavaScript. When you make a copy, it will be a real copy. Let’s see an example:

const a = 5
let b = a // this is the copy
b = 6
console.log(b) // 6console.log(a) // 5

By executing b = a , you make the copy. Now, when you reassign a new value to b, the value of b changes, but not of a.

Composite data types — Objects and Arrays

In These values are actually stored just once when instantiated, and assigning a variable just creates a pointer (reference) to that value.

E.g

const a = { en: 'Hello', de: 'Hallo', es: 'Hola', pt: 'Olà'}let b = ab.pt = 'Oi'console.log(b.pt) // Oiconsole.log(a.pt) // Oi

In the example above, we actually made a shallow copy. This is often times problematic, since we expect the old variable to have the original values, not the changed ones.

How to handle this problem?

Spread operator

Introduced with ES2015, this operator is just great, because it is so short and simple. It ‘spreads’ out all of the values into a new object. You can use it as follows:

const a = { en: 'Hello', de: 'Hallo', es: 'Hola', pt: 'Olà'}let b = {...a}b.pt = 'Oi'console.log(b.pt) // Oiconsole.log(a.pt) // Ola

You can also use it to merge two objects together, for example const c = {...a, ...b} .

Object.assign

This was mostly used before the spread operator was around, and it basically does the same thing.

const a = { en: 'Hi', de: 'bye' }let b = Object.assign({}, a)b.de = 'tata'console.log(b.de) // tataconsole.log(a.de) // bye

Nested Objects

const a = { foods: { dinner: 'Pasta' } }let b = {...a}b.foods.dinner = 'Soup' // changes for both objectsconsole.log(b.foods.dinner) // Soupconsole.log(a.foods.dinner) // Soup

To make a deep copy of nested objects, you would have to consider that. One way to prevent that is manually copying all nested objects:

const a = { foods: {dinner: 'Pasta'}}let b = {foods: {...a.foods}}b.foods.dinner = 'Soup'console.log(b.foods.dinner) // Soupconsole.log(a.foods.dinner) // Pasta

Making deep copies without thinking

What if you don’t know how deep the nested structures are? It can be very tedious to manually go through big objects and copy every nested object by hand. There is a way to copy everything without thinking. You simply stringify your object and parse it right after:

const a = {foods: {dinner: 'Pasta'}}let b = JSON.parse(JSON.stringify(a))b.foods.dinner = 'Soup'console.log(b.foods.dinner) // Soupconsole.log(a.foods.dinner) // Pasta

copying instance of custom classes

class Counter { constructor() { this.count = 5}            copy()             
{
const copy = new Counter();
copy.count = this.count;
return copy
}
}const originalCounter = new Counter()const copiedCounter = originalCounter.copy()console.log(originalCounter.count) // 5console.log(copiedCounter.count) // 5copiedCounter.count = 7console.log(originalCounter.count) // 5console.log(copiedCounter.count) // 7

--

--