JavaScript 5 ways to deep copy objects

JavaScript 5 ways to deep copy objects

2023-07-02
4 min read

introduction

This article is going to talk about 5 ways to depp copy objects in JavaScript. Before we get started, we will look into what's the different between Shallow copy and Deep copy.

Shallow copy vs Deep copy

Shallow copy stores references of objects to the original memory address. On the other hands, Deep copy stores copies of the object's value

Shallow copy examples

js
const a = {
  name: 'a', 
}

const copies = a
cloned.name = 'new name'
console.log(a) // {name: "new name"}
console.log(cloned) // {name: "new name"}

cloned variable which is copied with = operator changed property name in a variable. Shallow copy reflects changes made to the new and copied object in the original object, but Deep copy doesn't reflect changes.

Deep copy

From now on, we are going to talk about Deep copy.

Deep copy - JSON.stringify() and JSON.parse()

JSON.stringify() method converts JavaScript value like primitive values, Object and Array to JSON string. JSON.parse() method parses a JSON string to JavaScript. It's contrary to JSON.stringify(). Combining both of these method allows to create a copy of an object.

js
const user = {
  name: 'Arthur',
  age: 20,
  address: {
    country: 'Canada'
  },
  setAge: function (newAge) {
    this.age = newAge
  }
}

const cloned = JSON.parse(JSON.stringify(user))
cloned.name = 'John'
cloned.address.country = 'Japan'
console.log(user) // {name: "Arthur", age: 20, address: {country: "Canada"}
console.log(cloned) // {name: "John", age: 20, address: {country: "Japan"}

You can see cloned doesn't change original value. It doesn't change nested object too. However, this way can't copy function value

js
const user = {
  name: 'Arthur',
  age: 20,
  address: {
    country: 'Canada'
  },
  setAge: function (newAge) {
    this.age = newAge
  }
}

const cloned = JSON.parse(JSON.stringify(user))
cloned.setAge(5)

You'll get TypeError: cloned.setAge is not a function error message.

Pros

  • Deep copies nested objects
  • Well known coding style

Cons

  • It doesn't copy functions
  • Low performance

Depp copy - Object.assign()

Object.assign() method copies all values from one or more source objects to a target object. It returns the modified target object.

js
const user = {
  name: 'Arthur',
  age: 20,
  address: {
    country: 'Canada'
  },
  setAge: function (newAge) {
    this.age = newAge
  }
}

const cloned = Object.assign({}, user)
cloned.name = 'John'
cloned.address.country = 'Japan'
cloned.setAge(5)
console.log(user) // {name: "Arthur", age: 20, address: {country: "Japan"}
console.log(cloned) // {name: "John", age: 5, address: {country: "Japan"}

Advantage of Object.assign() method is that Object.assign() method copies not only objects but also functions, so you can use function in object.

Pros

  • Deep copies objects and functions

Cons

  • It doesn't copy nested objects

Deep copy - The ... spread Syntax.

The ... spread syntax allows iterable to be expanded

js
const user = {
  name: 'Arthur',
  age: 20,
  address: {
    country: 'Canada'
  },
  setAge: function (newAge) {
    this.age = newAge
  }
}

const cloned = {...user}
cloned.name = 'John'
cloned.address.country = 'Japan'
cloned.setAge(5)
console.log(user) // {name: "Arthur", age: 20, address: {country: "Japan"}
console.log(cloned) // {name: "John", age: 5, address: {country: "Japan"}

Unfortunately, The spread syntax doesn't copy nested object like Object.assign() method. However, there is a way to copy nested object

js
const user = {
  name: 'Arthur',
  age: 20,
  address: {
    country: 'Canada'
  },
  setAge: function (newAge) {
    this.age = newAge
  }
}

const cloned = {...user,
  address: {
    ...user.address
  }
}
cloned.address.country = 'Japan'
console.log(user) // {name: "John", age: 20, address: {country: "Canada"}
console.log(cloned) // {name: "John", age: 20, address: {country: "Japan"}

If you use the spread syntax in nested object, they will be deep copied.

Pros

  • Deep copies objects and functions
  • Easy to use

Cons

  • Nested object can be deep copy, but it's very annoying to use.

Deep copy - lodash library

The lodash is a modern JavaScript utility library delivering modularity, performance & extras. The lodash provides utility called cloneDeep() method.

js
import _ from 'lodash'

const user = {
  name: 'Arthur',
  age: 20,
  address: {
    country: 'Canada'
  },
  setAge: function (newAge) {
    this.age = newAge
  }
}

const cloned = _.cloneDeep(user)
cloned.name = 'John'
cloned.address.country = 'Japan'
cloned.setAge(5)
console.log(user) // {name: "Arthur", age: 20, address: {country: "Canada"}
console.log(cloned) // {name: "John", age: 5, address: {country: "Japan"}

cloneDeep() method in lodash library allows deep copy, nested object and functions.

Pros

  • Deep copies objects, nested objects and functions

Cons

  • You should install the library, and it will increase your project size.

Deep copy structuredClone()

The structuredClone() global method creates a deep clone of a given value using the structured clone algorithm.

js
const user = {
  name: 'Arthur',
  age: 20,
  address: {
    country: 'Canada'
  },
  setAge: function (newAge) {
    this.age = newAge
  }
}

const cloned = structuredClone(user)
cloned.name = 'John'
cloned.address.country = 'Japan'
// cloned.setAge(5) // ERROR!
console.log(user) // {name: "Arthur", age: 20, address: {country: "Japan"}
console.log(cloned) // {name: "John", age: 5, address: {country: "Japan"}

Pros

  • Deep copies nested objects
  • Better performance than JSON.stringify() and JSON.parse().

Cons

  • It doesn't copy functions
  • It cannot clone DOM elements
  • Low version of browser and Node.js may not supported

Ref

Free open source project made by Youngjin