This will be difficult to do for any JavaScript object. The problem is that you will accidentally pick up attributes from an object’s prototype. These attributes should not be copied to the new instance. As some answers indicate, you’ll need to explicitly skip an attribute if you add a clone function to Object.prototype. What if you are not aware of any additional methods that have been added to Object.prototype? You will not copy any attributes that you don’t want, so use the hasOwnProperty function to find non-local, unforeseen attributes.
Hidden properties can be more difficult to copy than non-enumerable attribute attributes. A hidden property in a function, for example, is prototype. A prototype of an object is referenced by the attribute __proto__. This is also hidden and will not be copied using a for/in loop iterating through the attributes of the source object. Although __proto__ may only be available in Firefox’s JavaScript interpreter (and it could be different in other browsers), you get the idea. Some attributes are not enumerable. If you know the name of a hidden attribute, you can copy it. However, I don’t know how to automatically discover it.
Another problem in our quest for an elegant solution lies in correctly setting up the prototype inheritance. You can create a new general object by using if your source object is Object. However, if your source object is Object’s prototype, you will be missing any additional members that you didn’t use the hasOwnProperty filter for. You can call the constructor property of the source object to get the initial copy object, and then copy the attributes over. However, you will still not be able to get non-enumerable attribute. A Date object, for example, stores its data in a hidden member.
function clone(obj) { if (null == obj || "object" != typeof obj) return obj; var copy = obj.constructor(); for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr]; } return copy; } var d1 = new Date(); /* Executes function after 5 seconds. */ setTimeout(function(){ var d2 = clone(d1); alert("d1 = " + d1.toString() + "nd2 = " + d2.toString()); }, 5000);
The date string for d1 is 5 seconds slower than that of d2. The setTime method can be used to make one Date identical to another. However, it is only applicable to the Date class. Although I doubt there is a universal solution, I believe it can be done.
I made a compromise when I was asked to implement general deep copying. I assumed that I would only need a plain object, Array or date, string, number, or Boolean. These three types are immutable so I could do a shallow copy without worrying about it changing. Further, I assumed that elements in Object and Array would be included in one of the six simple types. You can achieve this with code such as the following:
function clone(obj) { var copy; // Handle the 3 simple types, and null or undefined if (null == obj || "object" != typeof obj) return obj; // Handle Date if (obj instanceof Date) copy = new Date(); copy.setTime(obj.getTime()); return copy; } // Handle Array if (obj instanceof Array) { copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = clone(obj[i]); } return copy; } // Handle Object if (obj instanceof Object) { copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]); } return copy; } throw new Error("Unable to copy obj! Its type isn't supported."); }
As long as data in arrays and objects form a tree structure, the above function should work for all six types. This means that there can be only one reference to the object’s data. Take this example:
// This would be cloneable: var tree = { "left" : { "left" : null, "right" : null, "data" : 3 }, "right" : null, "data" : 8 }; // This would kind-of work, but you would get 2 copies of the // inner node instead of 2 references to the same copy var directedAcylicGraph = { "left" : { "left" : null, "right" : null, "data" : 3 }, "data" : 8 }; directedAcyclicGraph["right"] = directedAcyclicGraph["left"]; // Cloning this would cause a stack overflow due to infinite recursion: var cyclicGraph = { "left" : { "left" : null, "right" : null, "data" : 3 }, "data" : 8 }; cyclicGraph["right"] = cyclicGraph;
Although it won’t be able handle JavaScript objects, it might be sufficient for other purposes provided you don’t assume it will work for everything.