P粉6919581812023-08-24 09:44:45
If you are not using Date, Function, Undefined, regExp or Infinity in the object, a very simple line 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' } 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()
This applies to all types of objects including objects, arrays, strings, booleans and numbers.
See also this article about the Structured Cloning Algorithm Browser , used when publishing messages to or from workers. It also includes deep cloning functionality.
P粉1229324662023-08-24 00:44:32
There is a new JS standard called Structured Cloning. It works in many browsers (see Can I use it).
const clone = structuredClone(object);
Doing this with any object in JavaScript is never going to be simple or straightforward. You'll run into the problem of incorrectly getting properties from the object's prototype, which should remain in the prototype instead of being copied to the new instance. For example, if you were to add a clone
method to Object.prototype
(as some answers suggest), you would need to explicitly skip that property. But what if there are other additional methods added in Object.prototype
or other intermediate prototypes that you don't know about? In this case, you are copying a property that shouldn't be copied, so you need to use the hasOwnProperty
method.
In addition to non-enumerable properties, you also run into a trickier problem when you try to copy an object with hidden properties. For example, prototype
is a hidden attribute of a function. Additionally, the object's prototype is referenced through the property __proto__
, which is also hidden and is not copied by for/in loops that iterate over the source object's properties. I think __proto__ may be specific to Firefox's JavaScript interpreter and may be different in other browsers, but you get the idea. Not everything is enumerable. If you know the name of the hidden property you can copy it, but I don't know of any way to discover it automatically.
Another obstacle in the search for an elegant solution is the problem of setting up prototypal inheritance correctly. If the source object's prototype is Object
, then just create a new generic object using {}
, but if the source object's prototype is Object
some descendant code>, then you will lose other members in that prototype that were skipped using the hasOwnProperty
filter, or other members in the prototype that were not enumerable in the first place. One solution might be to call the source object's constructor property to get the initial copy object and then copy the properties, but you still won't get the non-enumerable properties. For example, the Date code>
object stores its data as hidden members:
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
will be 5 seconds later than d2
. The way to make one Date
identical to another is to call the setTime
method, but this is specific to the Date
class. I don't think there's a foolproof universal solution to this problem, although I'm happy to be wrong!
When I had to implement a general deep copy, I finally compromised and assumed that I just needed to copy a normal Object
, Array
, Date代码>,<代码>String代码>, <代码>Number代码>, or <代码>Boolean代码>. The last 3 types are immutable so I can perform shallow copies without worrying about it changing. I further assume that any element contained in
Object
or Array
will also be one of the 6 simple types in this list. This can be done with code like this:
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 the data in the object and array form a tree structure, the above function is sufficient for the 6 simple types I mentioned. That is, the same data in the object is referenced no more than once. For 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;
It can't handle any JavaScript objects, but it may be sufficient for many purposes, as long as you don't think it will only work with anything you throw at it.