How to deep copy a custom object in JavaScript?
Asked Answered
S

6

12

I've been surfing around here a while and still haven't found an answer that worked for me.

Is there any way to deep copy a non-plain object in JS?

I've tried jQuery.extend(true, {}, this) but it only cloned some of it, the rest remained as a reference to another object.

Siskind answered 10/10, 2016 at 23:37 Comment(6)
"but it only cloned some of it, the rest remained as a reference to another object." Can you include object at Question, create stacksnippets to demonstrate?Salta
There is always the JSON.parse(JSON.stringify(...)) hack.Naominaor
@AkshatMahajanIf it is JSON format, that is fine, OP hints that it has functions.Karlis
@Siskind What is this at jQuery.extend(true, {}, this)?Salta
What do you mean by "non-plain object"? Do you mean things like DOM elements and functions? What would it mean to deep-copy those?Angulo
Does this answer your question? How to Deep clone in javascriptViable
Q
13

Here are 3 different methods for copying objects. Each method has pros and cons, so read through and pick the best for your situation

Object.assign method

Use Object.assign, which "is used to copy the values of all enumerable own properties from one or more source objects to a target object". This copies both values and functions. At the time of writing this, browser support is good but not perfect, but this is the best method IMO of the three.

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

Spread operator method

Alternatively, you can use the spread operator to spread from one object into another. Keep in mind that this will copy the values of keys, but if you the value of a key is a memory address (an other nested object or an array) then it will only be a shallow copy.

const obj1 = {a: () => {}, b:2}
const obj1Copy = { ...obj1 }

JSON stringify/parse trick

If the object doesn't have any circular references or functions as values, you can use the json stringify trick:

let myCopy = JSON.parse(JSON.stringify(myObject));

No libraries required, and works very well for most objects.

Quartz answered 10/10, 2016 at 23:40 Comment(10)
Will this copy functions at myObject?Salta
@Salta Nope. But why you add functions into structures of data? Functions must be as routines in different place. This is just OOP fail ))Inadvertency
@Inadvertency An example of an object having properties where functions are set as values would be document, or windowSalta
@Salta function != value. Clone document or window? Absolutely unusual. Ok. I understand e.g. window.onload but this is callbacks for events of observer. And theoretically we can implement functions without parent object, pass target object into first argument, like C89 style (e.g. onload(img, function(it){/*...*/})). I see only one profit for using functions in structures -- copy protect (e.g. google maps). Blame inurement. Now all devs use the functions of data structures instead routine functions.Inadvertency
@Inadvertency That is var obj = {"0":function(){}} where "0" is property, value of obj["0"] is function(){}. Or is this incorrect interpretation of example? "Clone document or window? Absolutely unusual." Can be useful for certain projects, see this Answer at Edit, save, self-modifying HTML document; format generated HTML, JavaScriptSalta
@Salta 1. incorrect. Function must be called. Value can't be called. 2. Like trying to write OS on php. ))Inadvertency
@Inadvertency What would set, get within object, or String, Array .protototype.length be considered?Salta
@Salta setSomething(targetObj, value) or length(value) be considered. Functions of objects can be named syntactic sugar.Inadvertency
1. JSON.parse is very inefficent. 2. I don't get why using methods on object is OOP fail. Actually recent standards in client side recommend using OOP which imply using methods. If you are talking about data objects it's ok but in the question there is no reference about this.Personnel
Object.assign and the spread operator method both produce shallow copies. JSON.stringify/parse does a deep copy, but it fails on several different data types (undefined, Date, Set, Map, etc.).Hemangioma
H
5

You can use lodash's cloneDeep function - https://lodash.com/docs/4.16.4#cloneDeep

Example (from docs)

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
Hackler answered 10/10, 2016 at 23:39 Comment(0)
C
2

You could use the structuredClone method:

const cloned = structuredClone(object)

Anyway, structuredClone allows you to do also other things that you might be interested in.

Check the documentation for further details:

https://developer.mozilla.org/en-US/docs/Web/API/structuredClone

Capparidaceous answered 15/8, 2022 at 18:6 Comment(0)
B
1

A quick method to clone objects deep with performance into consideration.

JSON.parse(JSON.stringify({"foo":"bar"}))

How about performance ? >> [ May be this is the best way to deep copy objects ]. I strongly recommend you to checkout this video from Google Chrome Developers community on Youtube explaining how this method works and performance benchmarks.

Note: Use the JSON.parse method if your objects don't have Dates, functions, undefined, Infinity, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays or other complex types. Source : Read this SO answer


Quick tip - React.JS initial state tree can be loaded from localStorage using this solution.

Brottman answered 23/11, 2019 at 3:6 Comment(3)
Note that JSON.stringify/parse always creates a plain Object, stripping out any custom methods that may have been in the original. To restore the methods to the copy after stringify/parse, use Object.setPrototypeOf(copy, Object.getPrototypeOf(original))Hemangioma
Also, correct me if I'm wrong, it seems to strip out any property which is undefined.Thumbnail
@Thumbnail I have stated in the Note sectionBrottman
L
0

If you are dealing with a class instance you could use something like this.

You wouldn't need to copy the functions as they are delegated to on the prototype.

// myObject constructor
function myObject(foo, bar){
  this.foo = foo
  this.bar = bar
}
// delegate the functions to a prototype
myObject.prototype.something = function(){
  console.log('something')
}

function instanceCopy(obj) {
  // copy the object by the constructor
  const copy = new obj.constructor()
  const keys = Object.keys(obj)
  keys.forEach(key => {
    copy[key] = obj[key]
  })
  return copy
}

const myObj = new myObject('foo', 'bar')
const copyObj = instanceCopy(myObj)

console.log('myObj', myObj)
console.log('copyObj', copyObj)
console.log('same ?', copyObj === myObj)

// can we still call the functions
copyObj.something()
<script src="https://codepen.io/synthet1c/pen/WrQapG.js"></script>
Luettaluevano answered 11/10, 2016 at 0:49 Comment(0)
G
-1

Lodash _.cloneDeep() method kills the application performance. So I have come up with basic JavaScript solution. I have added it to my GIT repo. My application performance is back to normal after using my solution.

https://github.com/manideeppabba1991/javascript_util_functions/blob/master/clone_Array_or_Object.js

Giddens answered 15/11, 2018 at 16:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.