Monday, May 20, 2024
 Popular · Latest · Hot · Upcoming
155
rated 0 times [  157] [ 2]  / answers: 1 / hits: 192585  / 14 Years ago, thu, december 16, 2010, 12:00:00

How do you deep clone a JavaScript object?


I know there are various functions based on frameworks like JSON.parse(JSON.stringify(o)) and $.extend(true, {}, o) but I don't want to use a framework like that.


What is the most elegant or efficient way to create a deep clone.


We do care about edge cases like cloning array's. Not breaking prototype chains, dealing with self reference.


We don't care about supporting copying of DOM objects and like because .cloneNode exists for that reason.


As I mainly want to use deep clones in node.js using ES5 features of the V8 engine is acceptable.


[Edit]


Before anyone suggests let me mention there is a distinct difference between creating a copy by prototypically inheriting from the object and cloning it. The former makes a mess of the prototype chain.


[Further Edit]


After reading your answer I came to the annoying discovery that cloning entire objects is a very dangerous and difficult game. Take for example the following closure based object


var o = (function() {
var magic = 42;

var magicContainer = function() {
this.get = function() { return magic; };
this.set = function(i) { magic = i; };
}

return new magicContainer;
}());

var n = clone(o); // how to implement clone to support closures

Is there any way to write a clone function that clones the object, has the same state at time of cloning but cannot alter the state of o without writing a JS parser in JS.


There should be no real world need for such a function anymore. This is mere academic interest.


More From » javascript

 Answers
35

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.


[#94581] Tuesday, December 14, 2010, 14 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
malkajillc

Total Points: 652
Total Questions: 107
Total Answers: 98

Location: Finland
Member since Sat, Nov 6, 2021
3 Years ago
malkajillc questions
;