Shallow Copy vs Deep Copy in JavaScript: Understanding Object Cloning

Darshit AnjariaDarshit Anjaria
4 min read

Introduction

Have you ever wondered why changing a copied object in JavaScript also affects the original one? This strange behavior occurs when you don't realize you're dealing with a shallow copy rather than a deep copy. In JavaScript, objects and arrays are reference types, which means that copying them isn’t always as simple as it might seem. Whether you're a beginner or an experienced developer, understanding shallow and deep copies is critical to preventing bugs and ensuring data integrity in your applications.

In this article, we’ll break down the concepts of shallow copy and deep copy, why it’s essential to grasp the difference, and when to use each approach in production.


Why We Need to Understand the Concept

At the core of JavaScript lies the concept of reference types for objects, arrays, and functions. When you assign or copy these types, you aren't just duplicating values; you may be sharing references to the same memory location. This can lead to unintended side effects, especially when working with complex data structures.

In day-to-day development, especially in scenarios involving data manipulation, React state management, or handling nested objects and arrays, knowing whether you’re making a shallow or deep copy can be the difference between smooth sailing and debugging nightmares.


Shallow Copy

A shallow copy means creating a new object, but the nested objects inside it are still referenced from the original object. In other words, only the top-level properties are copied by value, while the deeper levels are copied by reference.

Example of a Shallow Copy

const originalObject = {
  name: 'Darshit',
  age: 30,
  address: {
    city: 'Mumbai',
    country: 'India',
  },
};

// Shallow copy using Object.assign
const shallowCopy = Object.assign({}, originalObject);

shallowCopy.address.city = 'Pune';

console.log(originalObject.address.city); // Output: 'Pune'
console.log(shallowCopy.address.city); // Output: 'Pune'

In the example above, we made a shallow copy using Object.assign(). However, the address property was not deeply copied. Changing shallowCopy.address.city also altered originalObject.address.city, since they both reference the same object.


Deep Copy

A deep copy means duplicating the entire structure of an object, including nested objects. This way, no references to the original object are preserved, and changes to the copied object won’t affect the original one.

Example of a Deep Copy

const originalObject = {
  name: 'Darshit',
  age: 30,
  address: {
    city: 'Mumbai',
    country: 'India',
  },
};

// Deep copy using JSON methods
const deepCopy = JSON.parse(JSON.stringify(originalObject));

deepCopy.address.city = 'Pune';

console.log(originalObject.address.city); // Output: 'Mumbai'
console.log(deepCopy.address.city); // Output: 'Pune'

Here, we’ve created a deep copy using JSON.stringify() and JSON.parse(). Now, modifying the deepCopy object has no impact on originalObject.


Production Use-Case

Let’s imagine you're building an e-commerce platform. You have a shopping cart object that holds details of selected products, their quantities, and prices. When a user decides to update their cart, you need to create a new instance of the cart while preserving the original one for comparison or rollback purposes.

const originalCart = {
  items: [
    { productId: 1, quantity: 2 },
    { productId: 2, quantity: 1 },
  ],
  totalPrice: 150,
};

// Deep copy to save cart state before modification
const updatedCart = JSON.parse(JSON.stringify(originalCart));

updatedCart.items[0].quantity = 3;

console.log(originalCart.items[0].quantity); // Output: 2
console.log(updatedCart.items[0].quantity); // Output: 3

In a scenario like this, a deep copy is crucial to ensure that changes to updatedCart don’t unintentionally affect originalCart. If you mistakenly used a shallow copy, any updates made to updatedCart would reflect on originalCart, potentially causing data inconsistencies.


When You Can’t Use Deep Copy

While deep copying can safeguard your data integrity, it’s not always suitable. Performance concerns arise when dealing with large and complex objects. Using methods like JSON.parse(JSON.stringify()) for deep copying may introduce significant performance overhead. This method also fails to handle functions, undefined, or special object types like Map or Set.

In real-time applications, such as game development or financial trading platforms, where speed and memory optimization are crucial, deep copying can slow down the system. Instead, careful management of shallow copies and ensuring proper reference handling would be a more effective approach.


Conclusion

Understanding the difference between shallow and deep copy is vital for avoiding unwanted side effects and ensuring your data remains consistent, especially when working with nested structures. Shallow copies can be efficient in simple use cases, but deep copies are necessary when working with more complex, multi-level objects that you don't want to mutate inadvertently.

In production environments, knowing when to opt for a shallow copy over a deep copy — and being aware of the limitations of deep copy methods — will save you from performance bottlenecks and subtle bugs.


Thank You!

Thank you for reading!
I hope you enjoyed this post. If you did, please share it with your network and stay tuned for more insights on software development. I'd love to connect with you on LinkedIn or have you follow my journey on HashNode for regular updates.

Happy Coding!
Darshit Anjaria

0
Subscribe to my newsletter

Read articles from Darshit Anjaria directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Darshit Anjaria
Darshit Anjaria

Experienced professional with 4.5+ years in the industry, collaborating effectively with developers across domains to ensure timely project delivery. Proficient in Android/Flutter and currently excelling as a backend developer in Node.js. Demonstrated enthusiasm for learning new frameworks, with a quick-learning mindset and a commitment to writing bug-free, optimized code. Ready to learn and adopt to cloud technologies.