How to Copy Objects in JavaScript: A Comprehensive Guide

In JavaScript, objects are reference types, meaning that when you assign an object to another variable, you’re not creating a new copy but rather a reference to the same object. This can lead to unintended side effects if you modify the object later. To avoid this, you often need to create copies of objects. There are two main types of object copying: shallow copy and deep copy. Let’s explore both and learn how to implement them effectively.

What is a Shallow Copy?

A shallow copy creates a new object and copies the references of the original object’s properties into the new object. This means that if the object contains nested objects or arrays, those nested structures are still referenced, not copied. Modifying a nested object in the original will affect the copy.

1. Using Object Literal

You can create a new object and manually copy each property from the original object.

const original = { a: 1, b: 2 };
const copy = { a: original.a, b: original.b };

console.log(copy); // Output: { a: 1, b: 2 }

Pros: Simple and straightforward for small objects.
Cons: Not efficient for large or complex objects.

2. Using Object.assign()

Object.assign() copies all enumerable own properties from the source object(s) to the target object.

const original = { a: 1, b: 2 };
const copy = Object.assign({}, original);

console.log(copy); // Output: { a: 1, b: 2 }

Pros: Concise and works for multiple sources.
Cons: Only performs a shallow copy.

3. Using Spread Operator (...)

The spread operator can be used to create a shallow copy of an object.

const original = { a: 1, b: 2 };
const copy = { ...original };

console.log(copy); // Output: { a: 1, b: 2 }

Pros: Clean and modern syntax.
Cons: Shallow copy only.

4. Using JSON.parse() and JSON.stringify()

This method converts the object to a JSON string and then back into an object, effectively creating a shallow copy.

const original = { a: 1, b: 2 };
const copy = JSON.parse(JSON.stringify(original));

console.log(copy); // Output: { a: 1, b: 2 }

Pros: Handles nested objects (but only at the top level; deeper nested objects are still references).
Cons: Cannot copy functions, symbols, or undefined values.

What is a Deep Copy?

A deep copy creates a new object and recursively copies all nested objects and arrays. This ensures that changes to nested structures in the original object do not affect the copy.

1. Using JSON.parse() and JSON.stringify() for Deep Copy

While the JSON method creates a shallow copy, it can sometimes be used for deep copies if the object structure is simple and doesn’t contain functions or other non-serializable data.

const original = { a: 1, b: { c: 2 } };
const copy = JSON.parse(JSON.stringify(original));

original.b.c = 3;
console.log(copy.b.c); // Output: 2

Pros: Simple to implement.
Cons: Limited to serializable data and may not handle complex structures or circular references.

2. Using Third-Party Libraries

Libraries like lodash provide methods for deep cloning objects.

const _ = require('lodash');
const original = { a: 1, b: { c: 2 } };
const copy = _.cloneDeep(original);

original.b.c = 3;
console.log(copy.b.c); // Output: 2

Pros: Handles complex structures and circular references.
Cons: Adds a dependency to your project.

3. Writing a Custom Deep Copy Function

You can write a function to perform a deep copy by recursively checking each property.

function deepCopy(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  const copy = Array.isArray(obj) ? [] : {};

  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      copy[key] = deepCopy(obj[key]);
    }
  }

  return copy;
}

const original = { a: 1, b: { c: 2 } };
const copy = deepCopy(original);

original.b.c = 3;
console.log(copy.b.c); // Output: 2

Pros: Full control over the copying process.
Cons: Can be complex and may not handle all edge cases like circular references.

Frequently Asked Questions

1. What is the difference between a shallow copy and a deep copy?

  • Shallow Copy: Copies the top-level properties. Nested objects are still referenced.
  • Deep Copy: Creates copies of all nested objects, ensuring complete independence between the original and the copy.

2. When should I use a shallow copy versus a deep copy?

  • Use a shallow copy when you don’t have nested objects or when you want the nested objects to be shared between the original and the copy.
  • Use a deep copy when you need the copy to be completely independent of the original, especially when dealing with nested structures.

3. Can I use the spread operator for deep copies?

  • No, the spread operator only performs a shallow copy. It does not copy nested objects deeply.

4. How do I handle circular references when performing a deep copy?

  • Circular references occur when an object references itself, either directly or indirectly. Detecting and handling circular references in a deep copy function can be complex. Libraries like lodash handle this internally, so it’s often easier to use them for such cases.

5. What are immutable objects, and how do they affect copying?

  • Immutable objects cannot be changed after they are created. When you copy an immutable object, you are essentially creating a new object with the same value. In JavaScript, primitives like numbers and strings are immutable, while objects and arrays are mutable.

Conclusion

Copying objects in JavaScript is a fundamental skill that every developer should master. Whether you need a shallow or deep copy depends on your specific use case. For simple objects, a shallow copy using the spread operator or Object.assign() is sufficient. For more complex structures, consider using the JSON method, third-party libraries, or writing a custom deep copy function. Always test your copying logic to ensure it behaves as expected, especially when dealing with nested objects or arrays.

By understanding these techniques, you can write more robust and maintainable JavaScript code.

Index
Scroll to Top