DEV Community

loading...

What's the best way to deeply clone an object in JavaScript?

jasterix profile image Jasterix ・1 min read

I recently attempted a code challenge where I needed to deeply clone an object. Prior to this I learned to do this by converting it to JSON. But according to this blog post, he specifically has JSON Serialization as the wrong way to do this.

The reason for this is:

"By doing this you will lose any Javascript property that has no equivalent type in JSON, like Function or Infinity. Any property that’s assigned to undefined will be ignored by JSON.stringify, causing them to be missed on the cloned object.

Also, some objects are converted to strings, like Date objects for example (also, not taking into account the timezone and defaulting to UTC), Set, Map and many others"

Is there a native way to deeply clone an object in JavaScript? The only approach he listed is by using the Lodash library. All of the other methods are shallow copies.

Discussion

pic
Editor guide
Collapse
masterroshan profile image
TJ Johnson

A library is just a collection of solutions written in a way that they can be reusable. Here is the module in lodash that contains the logic for the deep clone github.com/lodash/lodash/blob/mast..., it's imported from this module github.com/lodash/lodash/blob/mast..., which is the module you would use in your code.

Collapse
patarapolw profile image
Pacharapol Withayasakpunt

There seems to be no native way, but it is as simple as,

function deepClone(o) {
  /**
   * This excludes null
   */
  if (o && typeof o === 'object') {
    if (Array.isArray(o)) {
      return o.map((a) => deepClone(a))
    } else if (o.constructor === Object) {
      return Object.entries(o).reduce((prev, [k, v]) => ({ ...prev, [k]: deepClone(v) }), {})
    }

    /**
     * Now it depends on how you would recreate the Object, or not...
     */
    return o
  }

  /**
   * BTW, function is another important case you might consider...
   */
  return o
}

A perfect way is simply,

/**
 * Yes, that dangerousLoad
 * js-yaml's dump also supports several options
 */
yaml.load(yaml.dump(o))
Collapse
jasterix profile image
Jasterix Author

This is helpful. Thanks!!

Collapse
pengeszikra profile image
Peter Vivo

Are you means?

const base = {
  a: {
   b: [1,2,4],
   c: 'call me c'
  }, 
  d: p => p * 2, 
  get orig(){ return base}
};

const result = deepCopy(base);

test cases (mandatory true)

  result.a !== test.a
  result.a.b !== test.a.b
  result.a.c === test.a.c
  result.d !== test.d
  result.d(42) === test.d(42)
  test.orig === test
  result.orig === test
  result.orig !== result
Collapse
jasterix profile image
Jasterix Author

This is awesome! Thanks for also adding the test cases

Collapse
jalal246 profile image
Jalal πŸš€
const deeplyCloned = object.assign({}, myObj)
Collapse
ecabr profile image
Edgar

The method Object.assign() is not a "full" deep clone, works only at the first level. Example here. Maybe recursive Object.assign() does the job.

Collapse
jalal246 profile image
Jalal πŸš€

cloning nested object is like diving into a black hole

Collapse
penguinsource profile image
Mihai

If it's not a nested object, it's really nice to use the spread operator
const newObject = { ..currentObject };

If it's a nested or deeply nested object, it's much safer to use
const newObject = JSON.parse(JSON.stringify(currentObject))

in a nutshell:
const cloneDeep = (obj) => {
return JSON.parse(JSON.stringify(obj))
}

I should say i haven't taken the time to see any performance detriments, if any, appear.

Collapse
kevindsteeleii profile image
Kevin Steele

let obj = {stuff: {
more_stuff: "this",
list: [1,2,3]},
}

JSON.parse(JSON.stringify(obj))

Collapse
jasterix profile image
Jasterix Author

This is only a shallow clone so nested reference types would not be cloned