How to Clone an Object in JavaScript

copy object javascript
Table of Contents

Cloning or duplicating objects in JavaScript is a common task for developers who need to avoid unintended side effects when sharing data. If you simply use the assignment operator to reference one object from another, you risk mutating the same object instead of creating a new object. By learning how to copy an object correctly, you can keep your code clean, ensure data integrity, and handle everything from simple object literals to more advanced scenarios such as nested objects. In this article, you’ll discover practical methods for copy object javascript, including both shallow copy and deep copy techniques, and see how to deal with edge cases like circular references or an undefined value.

Key Concepts and Terms

  • Objects in JavaScript can store a wide range of value types, including primitive value (e.g., string or number) and reference value (e.g., arrays or other objects).
  • A shallow copy copies only top-level properties, so nested properties still point back to the original object.
  • A deep copy ensures that all the elements of nested objects are cloned, producing a new object completely independent of the original object.

Below, we’ll walk through three methods (among others) to clone JavaScript objects. Our focus will be on shallow copy example approaches and deep copy example approaches, plus some advanced considerations.


Using Object.assign()

One straightforward way to copy objects is to use the Object.assign() method. It takes a target object as its first argument, followed by one or more source objects, and then returns the modified target.

const personObject = { 
  name: 'Alice', 
  info: { city: 'Wonderland' }
}

const clonedPerson = Object.assign({}, personObject)

console.log('Cloned Person:', clonedPerson) // Cloned Person: {name: 'Alice', info: {city: 'Wonderland'}}
  • This approach creates a shallow copy, so any nested properties remain linked to the original object.
  • If you modify info.city in clonedPerson, it affects the personObject as well because those properties share the same reference.
  • Despite this limitation, Object.assign() is a handy way to create shallow copies if you only need top-level duplication.
  • Internally, Object.assign() copies only enumerable own properties from the source objects and will skip any non enumerable property or symbol values that are not enumerated.

Using the Spread Operator

The spread operator (...) also offers an easy way to copy objects:

const personObject = { 
  name: 'Alice', 
  info: { city: 'Wonderland' }
}

const spreadCopy = { ...personObject }

console.log('Spread Copy:', spreadCopy) // Spread Copy: {name: 'Alice', info: {city: 'Wonderland'}}


spreadCopy.info.city = 'New City'

console.log('Modified Spread:', spreadCopy) // Modified Spread: {name: 'Alice', info: {city: 'New City'}}
console.log('Original:', personObject) // Original: {name: 'Alice', info: {city: 'New City'}}
  • Like Object.assign(), this results in a shallow copy. Changing info still mutates the original object’s nested data.
  • Nonetheless, the spread operator is concise and commonly used to produce a quick new object with the same top level properties.

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

For a simple deep clone, many developers rely on json methods like JSON.stringify() and JSON.parse(). You can combine them (sometimes called json.parse json.stringify or parse json stringify) to convert your obj object to a string, then back to a plain object:

const personObject = { 
  name: 'Alice', 
  info: { city: 'Wonderland' }
}
const jsonClone = JSON.parse(JSON.stringify(personObject))

console.log('JSON Clone:', jsonClone) // JSON Clone: {name: 'Alice', info: {city: 'Wonderland'}}


jsonClone.info.city = 'Different City'

console.log('Modified JSON Clone:', jsonClone) // Modified JSON Clone: {name: 'Alice', info: {city: 'Different City'}}
console.log('Original Person Object:', personObject) // riginal Person Object: {name: 'Alice', info: {city: 'Wonderland'}}
  • This technique performs a deep copy of nested objects, giving you an exact copy that shares no references with the original object.
  • However, it cannot handle certain data types like functions, date object, or circular references gracefully. You lose any non enumerable property or methods in the process.

Best Practices and Advanced Techniques

  • If you need a deep clone while preserving all enumerable property details or handling advanced structures, you might explore specialized libraries or custom copy methods.
  • Object.assign() and the spread operator both assign properties onto an empty object if you provide {} as the target object. This is typically safer than merging into an existing structure.
  • When you have one object that merges multiple source objects, you must watch for conflicts of same value keys.
  • The Object.assign() method can also add new properties to your target object.
  • Keep in mind that obj’s prototype chain or object stores (e.g., references) remain relevant if you are not performing a full deep copy.

Here’s a quick function that manually handles some edge cases. It checks reference vs primitive value, deals with a date object, and recursively creates a deep clone:

function deepClone(sourceValue) {
  if (sourceValue === null) {
   return null
  }

  if (typeof sourceValue !== 'object') {
    return sourceValue
  }
  
  if (sourceValue instanceof Date) {
    return new Date(sourceValue.getTime()) // new date
  }

  const clone = Array.isArray(sourceValue) ? [] : {}
  for (const key in sourceValue) {
    clone[key] = deepClone(sourceValue[key])
  }

  return clone
}

This method will handle nested objects, but you still need to watch for circular references, where other objects may reference each other in loops.

Case Study: Cloning a Person Object with Object.assign()

Consider a scenario with two source objects describing a person object:

const personDetails = { name: 'Bob', age: 30 }
const personAddress = { info: { city: 'Example City' } }

const combinedPerson = Object.assign({}, personDetails, personAddress)

console.log('Combined Person:', combinedPerson) // Combined Person: {name: 'Bob', age: 30, info: {city: 'Example City'}}

Here, combinedPerson merges all the elements from the second object with the first. Although it may look like a new object, the info property is still a shallow copy. This approach works fine if you do not need to clone nested properties in depth.

Summary

Cloning is a critical aspect of working with objects in JavaScript. Depending on whether you want a shallow copy or a deep copy, you can pick from Object.assign(), the spread operator, JSON.stringify() and JSON.parse(), or specialized utilities. For basic needs, Object.assign() and spread syntax are simple ways to create a new object from a target object. If you require a thorough deep clone of nested objects, you’ll likely rely on parse json stringify or write your own function that carefully copies each property.

Next Steps

  • Explore the official documentation for Object.assign() and advanced cloning techniques in the CoreUI blog or other reputable sources.
  • Check out specialized libraries that handle advanced cloning of circular references and exotic data.
  • Practice by cloning a variety of objects (including object literals and arrays) to see how they react when you mutate the clone vs. the original object.

For even more details, consider tutorials or courses on JavaScript data structures and in-depth coverage of json stringify approaches. Remember, ensuring you assign carefully means you won’t unintentionally overwrite data you intended to preserve, and adapting your code for specific edge cases is key to robust applications.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.