So I'm always looking for a way to use vanilla JS whenever possible these days, and I discovered that deep copying an object in JavaScript is still weird.
StackOverflow reminded me of the JSON.parse( JSON.stringify( obj ) )
trick, and it looks like Object.assign
still doesn't copy nested objects.
jQuery's $.extend()
works. But that's not vanilla JS any more.
What hacks do you guys use for copying JS objects?
¯\_(ツ)_/¯
Top comments (18)
I use spread to copy and assign a new value to the old object
but this is the same as
Object.assign({}, obj);
, is not a deep copy.In plain javascript, there is only one option.
I don't know how and why, but in some cases Object.assign is working, but it's not recommended for use without deep understanding of it.
This is great for simple object literals. But, if someone out there tries it and it doesn't work, allow me to offer some reasons why.
JSON.parse
will fail for any object that recurses on itself. Try this in your console:var a = {}; a.a = a; JSON.stringify(a);
. This is uncommon in app-level code, but happens a lot in DOM parsing libraries and frameworks.toJSON()
method will be invoked and its response will be assumed to be correct JSON, whether or not it corresponds to the actual properties of the object.To cover your own butt, you're better off doing as others have suggested and using jQuery or Lodash. In modern ES6 code, you don't have to worry about code bloat, since you can just
import
the method you want to use from either toolkit and the rest of the library won't get included in your browser bundle.I would simply use recursion for a deep copy:
Well I suppose it depends on how deep the objects are and whether you have control over the organization of the objects. I believe for simple scenarios, you could do something along the following. Dunno if you qualify ES6 as Vanilla JS, so here's some ES5:
The output to the console:
Alternatively, if you have to handle dynamic objects of unknown dimensions, you could use a recursive function that uses Object.getOwnPropertyNames and the prototypes (someObject.__proto__) of those properties to handle the generation of fresh Objects for the properties of the destination object.
Hope that answers your question!
Edit: Added console output for the above script.
That does indeed look like a good solution if you know the object props.
But
JSON.parse( JSON.stringify( obj ) );
, though hacky, is much quicker to write!Oh okay. I misunderstood, I thought you were avoiding using that
JSON.parse( JSON.stringify( obj ) );
for some reason. Whoops!Have a good Turkey day, or a good Thursday.
Good to see I'm not crazy!
JSON.parse( JSON.stringify( obj ) );
seems to be a common 'shortcut'. But assigning each prop to a new object withobj.hasOwnProperty(item)
also looks like a good option if you know which properties to look for.I'm all in for vanilla JS but to a point, I would suggest copy the function from Lodash or jQuery. Use the code that was written and tested over the years, circular references can be a pain in the ..code.
Good ol'
var copy = {}; for (var item in obj) { obj.hasOwnProperty(item) && (copy[item] = obj[item]); }
approach works most of the times.About those cases when this doesn't work: maybe you're solving the wrong problem.
Oh boy...
I saw a lot of comments, many with arguments like "if you do this it doesn't work". Well, please note that isn't a "not working" subject but BY DESIGN. Deep copy will not keep references for example, and thus will not be carried along. Shallow copies will keep references and so can make shit happens if you're not aware of its behaviour. It's a matter of understanding exactly what you need and what you're cloning/copying.
Oh and BTW, undefined isn't equal to null. Undefined is a global object with a primitive "undefined" value, when setting something to undefined you are setting a reference to the undefined global object and a deep copy will not carry it along just like it wouldn't with any other references.
Keep that in mind.
I use this utility function for deep cloning the objects in vanilla JavaScript. It handles string properties, symbol properties and circular references in the input object.
So you're saying
Object.assign({}, obj);
doesn't work?It does work, but not for nested objects. i.e.:
Yeah it doesn't work on nested objects.