What is Deep Copy in JavaScript?

I recently wrote about Shallow Copy and I think with the shallow copy you should also understand about Deep Copy. So here is my latest post related to Deep Copy. What is Deep Copy? For ob ...

I recently wrote about Shallow Copy and I think with the shallow copy you should also understand about Deep Copy. So here is my latest post related to Deep Copy.


# What is Deep Copy?

  • For objects and array containing other objects or arrays, copying thee objects requires a deep copy. Otherwise, changes made to the nested references will change the data nested in the original object or array.
  • This is compared to a shallow copy, which works fine for an object or array containing only primitive values, but will fail for any object or array that has nested references to other objects or arrays.
  • Understanding the differences between == and === can help visually see the difference between shallow and deep copy, as the strict equality operator (===) shows that the nested references are the same:

# Example

const nestedArray = [["πŸ˜‰", "πŸ™‚", "😎"]]
const nestedCopyWithSpeed = [...nestedArray]
console.log(nestedArray[0] === nestedCopyWithSpread[0]) // true -- Shallow Copy (Same reference)
// This is a hack to make a deep copy that is not recommended because it will often fail:
const nestedCopyWithHacl = JSON.parse(JSON.stringify(nestedArray))
console.log(nestedArray[0] === nestedCopyWithHack[0]) // false -- Deep Copy (Different references)

# Let's see some methods for making a deep copy

# Deep copy with lodash

The library lodash is the most common way JavaScript Developers make a deep copy. It is surprisingly easy to use:

import_from "lodash" // Import the entire lodash library
const nestedArray = [["πŸ˜‰", "πŸ™‚", "😎"]]
const notACopyWithEquals = nestedArray
console.log(nestedArray[0] === notACopyWithEquals[0]) // true
const shallowCopyWithSpread = [...nestedArray]
console.log(nestedArray[0] === shallowCopyWithSpread[0]) // true
const shallowCopyWithLodashClone = _.clone(nestedArray)
console.log(nestedArray[0] === shallowCopyWithLodashClone[0]) // true
const shallowCopyWithLodashCloneDeep = _.cloneDeep(nestedArray)
console.log(nestedArray[0] === shallowCopyWithLodashCloneDeep[0]) // false
nestedArray[0] = "🧐"
nestedArray[2][0] = "😑"
console.log(...nestedArray) // 🧐 ["πŸ™‚"]["😑"]
console.log(...notACopyWithEquals) // 🧐 ["πŸ™‚"]["😑"]
console.log(shallowCopyWithSpread) // ["πŸ˜‰"]["πŸ™‚"]["😑"]
console.log(...shallowCopyWithLodashClone) // ["πŸ˜‰"]["πŸ™‚"]["😑"]
console.log(...deepCopyWithLodashCloneDeep) //  ["πŸ˜‰"]["πŸ™‚"]["😎"]

# Deep copy with Ramda

The functional programming library Ramda includes the R.clone() method, which makes a deep copy of an object or array.

import R from "ramda" // Import the entire ramda library
const nestedArray = [["πŸ˜‰", "πŸ™‚", "😎"]]
const notACopyWithEquals = nestedArray
console.log(nestedArray[0] === notACopyWithEquals[0]) // true
const shallowCopyWithSpread = [...nestedArray]
console.log(nestedArray[0] === shallowCopyWithSpread[0]) // true
const deepCopyWithRamdaClone = R.clone(nestedArray)
console.log(nestedArray[0] === deepCopyWithRamdaClone[0]) // false
nestedArray[0] = "🧐"
nestedArray[2][0] = "😑"
console.log(...nestedArray) // 🧐 ["πŸ™‚"]["😑"]
console.log(notACopyWithEquals) // 🧐 ["πŸ™‚"]["😑"]
console.log(shallowCopyWithSpread) // ["πŸ˜‰"]["πŸ™‚"]["😑"]
console.log(...deepCopyWithRamdaClone) // ["πŸ˜‰"]["πŸ™‚"]["😎"]

Read more - What Is Shallow Copy ?

# Deep copy with JSON.parse/stringify

If your data fits the specifications, then JSON.parse followed by JSON.strigify will deep copy your object.

// Only some of these will work with JSON.parse() followed by JSON.stringify()
const samplwObject = {
    string: 'string', 
    number: 123
    boolean: false, 
    null: null,
    notANumber: NaN, // NaN values will be lost  (The  value will be forced to 'null')
    date: new Date('2020-12-31T23:59:59')
    undefined: undefined, // Undefined values will be completely lost, including the key containing the undefined value
    infinity: Infinity, // Infinity will be lost (the value will be forced to 'null' )
    regExp: /.*/, // regExp will be lost (the value will be forced to an empty object{})
}
console.log(sampleObject) // Object
// {
//    string: "string", number: 123, boolean: false,   null: null, notANumber: NaN, date: Date Thu Dec 31 2020 23:59:59, undefined: undefined, infinity: Infinity, regExp: /.*/
// }
console.log(typeof sampleObject.date) // object
const faultyClone = JSON.parse(JSON.stringify(sampleObject)) 
console.log(faultyClone) // Object
// { 
//    string: "string", number: 123, boolean: false, null: null, notANumber: null, date: Date Thu Dec 31 2021 04:59:59, infinity: null, regExp: {}
// }
// The date object has been stringified. the result of .toISOString()
console.log(typeof faultyCloen.date) // string

If you don't wanna use Dates, functions, RegExps, Maps, Sets, Blobs, undefined, Infinity, [NaN], sparse arrays, typed arrays or any other complex types within your object then there is a very simple one-liner to deep clone an object is: ** JSON.parse(JSON.stringify(object)) **


😎Thanks For Reading | Happy Coding⚑