Shallow Vs Deep Copy In Javascript

Shallow Vs Deep Copy In Javascript

Hey, JS Developers!! While coding at least once you encounter a scenario where you need to copy an object or an array in Javascript. But sometimes it doesn't work as you expected.

In this article we'll cover the following concepts in detail:

  • Create a copy of Primitive Data types
  • Create a copy of Non-primitive data types
  • Shallow Copy
  • Methods to create a shallow copy of Array
  • Methods to create a shallow copy of Object
  • Deep Copy
  • Methods to create a deep copy

Let's get started!! 🚀🚀

🔢 Numbers

First, consider the simplest case of Numbers datatype.

// Case 1: Copy Numbers

let marks = 100;
let total = marks;  // copy marks into total
console.log(marks, total);

marks = 200; // change the value of marks
console.log(marks, total);

image.png

It will work as expected.

🧵 Strings

// Case 2: Copy Strings

let name = 'JS';
let name2 = name; //copy name into name2
console.log(name, name2);

name = 'blogs';
console.log(name, name2);

image.png

As you noticed in the above two scenarios if you change the value of an original variable it does not affect the other variable.

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.

📝 Arrays

Now let's see how it works in the case of Arrays:

// Case 3: Copy Arrays

let list = ['a', 'b', 'c', 'd'];
let box = list;
console.log("list-> ",list, "box-> ", box);

console.log("After changing some values ")

list[3] = 'e';
box[4] = 'f';
console.log("list-> ",list, "box-> ", box);

image.png

Wait what😲😲?? It doesn't yield the result we expected. If we update the array box it also changes the original array list. But why???

Because box is not a separate array, it is just a reference to the original array list. So whenever you update any of the arrays list or box it's always going to update the same reference.

But what we do if we don't want this behavior and only want to update our copied array without affecting the original one?

So here Shallow Copy comes into the picture.

Shallow Copy

Shallow copy is a bit-wise copy of an object. A new object is created that has an exact copy of the values in the original object. If any of the fields of the object are references to other objects, just the reference addresses are copied i.e., only the memory address is copied.

Using shallow copy, we can copy a value that is non-primitive (Array or Object), and ideally, all the values inside that array or object are primitive.

Let's explore some of the methods to make a copy of Arrays:

Methods to copy an Array :

👆 Method 1: use slice()

// Method 1: Use slice()

let list = ['a', 'b', 'c', 'd'];
let box = list.slice();
console.log("list-> ",list, "box-> ", box);

console.log("After changing some values ")

list[2] = 'e';
box[3] = 'f';
console.log("list-> ",list, "box-> ", box);

image.png

✌️ Method 2: use concat()

let list = ['a', 'b', 'c', 'd'];
let box = [].concat(list); // Method 2: Use concat()

console.log("list-> ",list, "box-> ", box);

console.log("After changing some values ")

list[2] = 'e';
box[3] = 'f';
console.log("list-> ",list, "box-> ", box);

image.png

🤟Method 3: ES6 Spread operator

let list = ['a', 'b', 'c', 'd'];
let box = [...list]; // Method 3: ES6 Spread operator

console.log("list-> ",list, "box-> ", box);

console.log("After changing some values ")

list[2] = 'e';
box[3] = 'f';
console.log("list-> ",list, "box-> ", box);

image.png

🖖Method 4: Array.from()


let list = ['a', 'b', 'c', 'd'];
let box = Array.from(list); // Method 4: Array.from()

console.log("list-> ",list, "box-> ", box);

console.log("After changing some values ")

list[2] = 'e';
box[3] = 'f';
console.log("list-> ",list, "box-> ", box);

image.png

🪕 Objects

Now let's try to understand the concept of shallow & deep copy in the case of Objects.

let student = {
    subject: 'JS',
    marks: 100
}

let record = student;

console.log("student",student, "record", record);
console.log("After changing some values ")

student.subject = 'Java';
record.marks = 50;
console.log("student", student, "record", record);

image.png

Ohh!! It shows the same behavior as Arrays. If we modify the record or student objects, changes are reflected on both of them. Because both of the objects refer to the same memory location.

image.png

Then how do we make a copy of an object🤔? Let's explore some of them:

Methods to copy an Object :

👆 Method 1: use Object.assign()

let student = {
    subject: 'JS',
    marks: 100
}

let record = Object.assign({},student); //Method 1: Use Object.assign()

console.log("student",student, "record", record);
console.log("After changing some values ")

student.subject = 'Java';
record.marks = 50;
console.log("student", student, "record", record);

image.png

✌️ Method 2: use spread operator

let student = {
    subject: 'JS',
    marks: 100
}

let record = {...student}; //Method 2: Use ES6 Spread Operator

console.log("student",student, "record", record);
console.log("After changing some values ")

student.subject = 'Java';
record.marks = 50;
console.log("student", student, "record", record);

image.png

So this is how Shallow Copy works.

Then what is Deep Copy and why do we use it? 🧐🧐

Deep Copy

Now let's explore in-depth what is Deep copy and why do we use it.

Deep copy is a process in which the copying process occurs recursively. It means first constructing a new collection object and then recursively populating it with copies of the child objects found in the original. In the case of deep copy, a copy of an object is copied into another object.

Let's try to understand this using an example:

let student = {
    subject: 'JS',
    marks: 100,
    details: {
        name: 'developer',
        age: 23
    }
}

let record = {...student}; //Method 2: Use ES6 Spread Operator

console.log("student",student, "\nrecord", record);

console.log("After changing some values ")
student.details.age = 30;
record.marks = 50;
console.log("student", student, "\nrecord", record);

image.png

image.png

As you noticed above when you change the value of age using student.details.age = 30; the change is reflected in both the objects.

But Why?

If you see the student object is having non-primitive values. It has a nested Object. Unlike the previous examples, this object did not have all primitive values in it. So, exactly in this kind of scenario, if we want to create a new copy of the student, shallow copying will no longer work.

In the above example, a separate copy is created for primitive values like subject & marks but details is a non-primitive value & its reference is stored instead of creating a new copy.

So, in order to copy all the sub-non-primitive values of a non-primitive value such as student, we need to use a copying technique called Deep Copying.

let student = {
    subject: 'JS',
    marks: 100,
    details: {
        name: 'developer',
        age: 23
    }
}

let record = JSON.parse(JSON.stringify(student)); //Deep Copy

console.log("After changing some values ")

student.details.age = 30;
record.marks = 50;
console.log("student", student, "\nrecord", record);

image.png

Now as you noticed in the above example, we have successfully copied the entire student object including its non-primitive nested objects values.

Summary

  • You really don't have to worry about copying primitive data types (strings/numbers) in JavaScript. When you make a copy, it will be a real copy.
  • Shallow copy is used to copy a non-primitive value such as array/object, which has all values of primitive type.
  • Deep Copy is also used to copy a non-primitive value such as an array/object which has non-primitive values in it such as a nested array/object.

Wrap Up!!

Thank you for your time!! Let's connect to learn and grow together.

LinkedIn Twitter

Buy-me-a-coffee