How to Deep clone in javascript
It really depends what you would like to clone. Is this a truly JSON object or just any object in JavaScript? If you would like to do any clone, it might get you into some trouble. Which trouble? I will explain it below, but first, a code example which clones object literals, any primitives, arrays and DOM nodes.
function clone(item) {
if (!item) { return item; } // null, undefined values check
var types = [ Number, String, Boolean ],
result;
// normalizing primitives if someone did new String('aaa'), or new Number('444');
types.forEach(function(type) {
if (item instanceof type) {
result = type( item );
}
});
if (typeof result == "undefined") {
if (Object.prototype.toString.call( item ) === "[object Array]") {
result = [];
item.forEach(function(child, index, array) {
result[index] = clone( child );
});
} else if (typeof item == "object") {
// testing that this is DOM
if (item.nodeType && typeof item.cloneNode == "function") {
result = item.cloneNode( true );
} else if (!item.prototype) { // check that this is a literal
if (item instanceof Date) {
result = new Date(item);
} else {
// it is an object literal
result = {};
for (var i in item) {
result[i] = clone( item[i] );
}
}
} else {
// depending what you would like here,
// just keep the reference, or create new object
if (false && item.constructor) {
// would not advice to do that, reason? Read below
result = new item.constructor();
} else {
result = item;
}
}
} else {
result = item;
}
}
return result;
}
var copy = clone({
one : {
'one-one' : new String("hello"),
'one-two' : [
"one", "two", true, "four"
]
},
two : document.createElement("div"),
three : [
{
name : "three-one",
number : new Number("100"),
obj : new function() {
this.name = "Object test";
}
}
]
})
And now, let's talk about problems you might get when start cloning REAL objects. I'm talking now, about objects which you create by doing something like
var User = function(){}
var newuser = new User();
Of course you can clone them, it's not a problem, every object expose constructor property, and you can use it to clone objects, but it will not always work. You also can do simple for in
on this objects, but it goes to the same direction - trouble. I have also included clone functionality inside the code, but it's excluded by if( false )
statement.
So, why cloning can be a pain? Well, first of all, every object/instance might have some state. You never can be sure that your objects doesn't have for example an private variables, and if this is the case, by cloning object, you just break the state.
Imagine there is no state, that's fine. Then we still have another problem. Cloning via "constructor" method will give us another obstacle. It's an arguments dependency. You never can be sure, that someone who created this object, did not did, some kind of
new User({
bike : someBikeInstance
});
If this is the case, you are out of luck, someBikeInstance was probably created in some context and that context is unkown for clone method.
So what to do? You still can do for in
solution, and treat such objects like normal object literals, but maybe it's an idea not to clone such objects at all, and just pass the reference of this object?
Another solution is - you could set a convention that all objects which must be cloned should implement this part by themselves and provide appropriate API method ( like cloneObject ). Something what cloneNode
is doing for DOM.
You decide.
What is the most efficient way to deep clone an object in JavaScript?
Native deep cloning
There's now a JS standard called "structured cloning", that works experimentally in Node 11 and later, will land in browsers, and which has polyfills for existing systems.
structuredClone(value)
If needed, loading the polyfill first:
import structuredClone from '@ungap/structured-clone';
See this answer for more details.
Older answers
Fast cloning with data loss - JSON.parse/stringify
If you do not use Date
s, functions, undefined
, Infinity
, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays or other complex types within your object, a very simple one liner to deep clone an object is:
JSON.parse(JSON.stringify(object))
const a = {
string: 'string',
number: 123,
bool: false,
nul: null,
date: new Date(), // stringified
undef: undefined, // lost
inf: Infinity, // forced to 'null'
re: /.*/, // lost
}
console.log(a);
console.log(typeof a.date); // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date); // result of .toISOString()
Deep copy in ES6 using the spread syntax
No such functionality is built-in to ES6. I think you have a couple of options depending on what you want to do.
If you really want to deep copy:
- Use a library. For example, lodash has a
cloneDeep
method. - Implement your own cloning function.
Alternative Solution To Your Specific Problem (No Deep Copy)
However, I think, if you're willing to change a couple things, you can save yourself some work. I'm assuming you control all call sites to your function.
Specify that all callbacks passed to
mapCopy
must return new objects instead of mutating the existing object. For example:mapCopy(state, e => {
if (e.id === action.id) {
return Object.assign({}, e, {
title: 'new item'
});
} else {
return e;
}
});This makes use of
Object.assign
to create a new object, sets properties ofe
on that new object, then sets a new title on that new object. This means you never mutate existing objects and only create new ones when necessary.mapCopy
can be really simple now:export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, object[key]);
return output;
}, {});
}
Essentially, mapCopy
is trusting its callers to do the right thing. This is why I said this assumes you control all call sites.
What is the most efficient way to deep clone an object in JavaScript?
Native deep cloning
There's now a JS standard called "structured cloning", that works experimentally in Node 11 and later, will land in browsers, and which has polyfills for existing systems.
structuredClone(value)
If needed, loading the polyfill first:
import structuredClone from '@ungap/structured-clone';
See this answer for more details.
Older answers
Fast cloning with data loss - JSON.parse/stringify
If you do not use Date
s, functions, undefined
, Infinity
, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays or other complex types within your object, a very simple one liner to deep clone an object is:
JSON.parse(JSON.stringify(object))
const a = {
string: 'string',
number: 123,
bool: false,
nul: null,
date: new Date(), // stringified
undef: undefined, // lost
inf: Infinity, // forced to 'null'
re: /.*/, // lost
}
console.log(a);
console.log(typeof a.date); // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date); // result of .toISOString()
JS | deep clone object partially
You can use spread syntax to combine shallow and deep copying.
let ob2 = {...obj1, deep_clone: JSON.parse(JSON.stringify(obj1.deep_clone)) };
...obj1
will shallow-copy the object, then the explicit assignment to the deep_clone:
property will replace that property with deep copy.
If the deep_clone
property is just one level deep, you can just use spread syntax there as well:
let ob2 = {...obj1, deep_clone: {...obj1.deep_clone} };
Related Topics
How to Check Whether a Radio Button Is Selected With JavaScript
How to Calculate Date Difference in JavaScript
Using Node.Js Require Vs. Es6 Import/Export
How to Set File Input Value When Dropping File on Page
How to Show Live Preview in a Small Popup of Linked Page on Mouse Over on Link
Fullpage.Js. Adding a Scroll Delay
How to Use JavaScript to Check and Load CSS If Not Loaded
How to Run Background Tasks in React Native
How to Sort Strings in JavaScript
Cross-Browser JavaScript Xml Parsing
Set Keyboard Caret Position in HTML Textbox
Correct Try...Catch Syntax Using Async/Await
What Are Alternatives to Document.Write
How to Use JavaScript Conditionally Like CSS3 Media Queries, Orientation
Serving Gzipped CSS and JavaScript from Amazon Cloudfront via S3
Add CSS Rule via Jquery for Future Created Elements
How to Have a Host and Container Read/Write the Same Files with Docker