Table of contents
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);
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);
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);
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);
✌️ 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);
🤟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);
🖖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);
🪕 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);
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.
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);
✌️ 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);
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);
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);
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.