我有一个对象, x 。 我想将其复制为对象y ,这样对y更改不会修改x 。 我意识到复制来自内置JavaScript对象的对象将导致额外的,不需要的属性。 这不是一个问题,因为我正在复制我自己的一个文字构造的对象。
如何正确克隆JavaScript对象?
I have an object, x. I'd like to copy it as object y, such that changes to y do not modify x. I realized that copying objects derived from built-in JavaScript objects will result in extra, unwanted properties. This isn't a problem, since I'm copying one of my own, literal-constructed objects.
How do I correctly clone a JavaScript object?
最满意答案
为JavaScript中的任何对象执行此操作并不简单或直接。 您将遇到错误地从对象原型中获取属性的问题,该原型应该保留在原型中,而不会复制到新实例。 例如,如果您将一个clone方法添加到Object.prototype ,如某些答案所描述的那样,您将需要显式地跳过该属性。 但是,如果还有其他附加方法添加到Object.prototype或其他中间原型,您不知道怎么办? 在这种情况下,您将复制不应该的属性,因此您需要使用hasOwnProperty方法检测不可预见的非本地属性。
除了不可枚举的属性之外,当您尝试复制具有隐藏属性的对象时,会遇到更严峻的问题。 例如, prototype是函数的隐藏属性。 另外,一个对象的原型被引用属性__proto__ ,它也是隐藏的,并且不会被一个for / in循环复制来遍历源对象的属性。 我认为__proto__可能是Firefox的JavaScript解释器特有的,它可能与其他浏览器不同,但是您可以看到图片。 不是一切都是无可否认的。 如果您知道其名称,您可以复制一个隐藏的属性,但我不知道有什么办法自动发现。
寻求优雅解决方案的另一个障碍是正确设置原型继承的问题。 如果您的源对象的原型是Object ,那么只需使用{}创建一个新的通用对象就可以正常工作,但是如果源的原型是Object后代,那么将要使用该跳过的原型丢失该原型的其他成员hasOwnProperty过滤器,或者在原型中,但不能枚举在第一位。 一个解决方案可能是调用源对象的constructor属性来获取初始复制对象,然后复制属性,但是您仍然不会获得不可枚举的属性。 例如, Date对象将其数据存储为隐藏成员:
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);d1的日期字符串将比d2稍后5秒。 使一个Date与另一个Date相同的方法是通过调用setTime方法,但这是特定于Date类的方法。 我不认为这个问题有一个防弹的一般解决方案,尽管我很乐意做错!
当我不得不实现一般的深层复制,我最终妥协,假设我只需要复制一个普通的Object , Array , Date , String , Number或Boolean 。 最后3种类型是不可变的,所以我可以执行一个浅的副本,而不用担心它的变化。 我进一步假设Object或Array包含的任何元素也将是该列表中6种简单类型之一。 这可以通过以下代码来实现:
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."); }上述功能将适用于我提到的6种简单类型,只要对象和数组中的数据形成一个树结构即可。 也就是说,对象中没有多于一个对同一数据的引用。 例如:
// 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 cylicGraph = { "left" : { "left" : null, "right" : null, "data" : 3 }, "data" : 8 }; cylicGraph["right"] = cylicGraph;它将无法处理任何JavaScript对象,但它可能足以满足许多目的,只要您不认为它将适用于您投掷的任何东西。
To do this for any object in JavaScript will not be simple or straightforward. You will run into the problem of erroneously picking up attributes from the object's prototype that should be left in the prototype and not copied to the new instance. If, for instance, you are adding a clone method to Object.prototype, as some answers depict, you will need to explicitly skip that attribute. But what if there are other additional methods added to Object.prototype, or other intermediate prototypes, that you don't know about? In that case, you will copy attributes you shouldn't, so you need to detect unforeseen, non-local attributes with the hasOwnProperty method.
In addition to non-enumerable attributes, you'll encounter a tougher problem when you try to copy objects that have hidden properties. For example, prototype is a hidden property of a function. Also, an object's prototype is referenced with the attribute __proto__, which is also hidden, and will not be copied by a for/in loop iterating over the source object's attributes. I think __proto__ might be specific to Firefox's JavaScript interpreter and it may be something different in other browsers, but you get the picture. Not everything is enumerable. You can copy a hidden attribute if you know its name, but I don't know of any way to discover it automatically.
Yet another snag in the quest for an elegant solution is the problem of setting up the prototype inheritance correctly. If your source object's prototype is Object, then simply creating a new general object with {} will work, but if the source's prototype is some descendant of Object, then you are going to be missing the additional members from that prototype which you skipped using the hasOwnProperty filter, or which were in the prototype, but weren't 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 over the attributes, but then you still will not get non-enumerable attributes. For example, a Date object stores its data as 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 will be 5 seconds behind that of d2. A way to make one Date the same as another is by calling the setTime method, but that is specific to the Date class. I don't think there is a bullet-proof general solution to this problem, though I would be happy to be wrong!
When I had to implement general deep copying I ended up compromising by assuming that I would only need to copy a plain Object, Array, Date, String, Number, or Boolean. The last 3 types are immutable, so I could perform a shallow copy and not worry about it changing. I further assumed that any elements contained in Object or Array would also be one of the 6 simple types in that list. This can be accomplished with code like 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."); }The above function will work adequately for the 6 simple types I mentioned, as long as the data in the objects and arrays form a tree structure. That is, there isn't more than one reference to the same data in the object. 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 will not be able to handle any JavaScript object, but it may be sufficient for many purposes as long as you don't assume that it will just work for anything you throw at it.
如何正确克隆JavaScript对象?(How do I correctly clone a JavaScript object?)我有一个对象, x 。 我想将其复制为对象y ,这样对y更改不会修改x 。 我意识到复制来自内置JavaScript对象的对象将导致额外的,不需要的属性。 这不是一个问题,因为我正在复制我自己的一个文字构造的对象。
如何正确克隆JavaScript对象?
I have an object, x. I'd like to copy it as object y, such that changes to y do not modify x. I realized that copying objects derived from built-in JavaScript objects will result in extra, unwanted properties. This isn't a problem, since I'm copying one of my own, literal-constructed objects.
How do I correctly clone a JavaScript object?
最满意答案
为JavaScript中的任何对象执行此操作并不简单或直接。 您将遇到错误地从对象原型中获取属性的问题,该原型应该保留在原型中,而不会复制到新实例。 例如,如果您将一个clone方法添加到Object.prototype ,如某些答案所描述的那样,您将需要显式地跳过该属性。 但是,如果还有其他附加方法添加到Object.prototype或其他中间原型,您不知道怎么办? 在这种情况下,您将复制不应该的属性,因此您需要使用hasOwnProperty方法检测不可预见的非本地属性。
除了不可枚举的属性之外,当您尝试复制具有隐藏属性的对象时,会遇到更严峻的问题。 例如, prototype是函数的隐藏属性。 另外,一个对象的原型被引用属性__proto__ ,它也是隐藏的,并且不会被一个for / in循环复制来遍历源对象的属性。 我认为__proto__可能是Firefox的JavaScript解释器特有的,它可能与其他浏览器不同,但是您可以看到图片。 不是一切都是无可否认的。 如果您知道其名称,您可以复制一个隐藏的属性,但我不知道有什么办法自动发现。
寻求优雅解决方案的另一个障碍是正确设置原型继承的问题。 如果您的源对象的原型是Object ,那么只需使用{}创建一个新的通用对象就可以正常工作,但是如果源的原型是Object后代,那么将要使用该跳过的原型丢失该原型的其他成员hasOwnProperty过滤器,或者在原型中,但不能枚举在第一位。 一个解决方案可能是调用源对象的constructor属性来获取初始复制对象,然后复制属性,但是您仍然不会获得不可枚举的属性。 例如, Date对象将其数据存储为隐藏成员:
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);d1的日期字符串将比d2稍后5秒。 使一个Date与另一个Date相同的方法是通过调用setTime方法,但这是特定于Date类的方法。 我不认为这个问题有一个防弹的一般解决方案,尽管我很乐意做错!
当我不得不实现一般的深层复制,我最终妥协,假设我只需要复制一个普通的Object , Array , Date , String , Number或Boolean 。 最后3种类型是不可变的,所以我可以执行一个浅的副本,而不用担心它的变化。 我进一步假设Object或Array包含的任何元素也将是该列表中6种简单类型之一。 这可以通过以下代码来实现:
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."); }上述功能将适用于我提到的6种简单类型,只要对象和数组中的数据形成一个树结构即可。 也就是说,对象中没有多于一个对同一数据的引用。 例如:
// 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 cylicGraph = { "left" : { "left" : null, "right" : null, "data" : 3 }, "data" : 8 }; cylicGraph["right"] = cylicGraph;它将无法处理任何JavaScript对象,但它可能足以满足许多目的,只要您不认为它将适用于您投掷的任何东西。
To do this for any object in JavaScript will not be simple or straightforward. You will run into the problem of erroneously picking up attributes from the object's prototype that should be left in the prototype and not copied to the new instance. If, for instance, you are adding a clone method to Object.prototype, as some answers depict, you will need to explicitly skip that attribute. But what if there are other additional methods added to Object.prototype, or other intermediate prototypes, that you don't know about? In that case, you will copy attributes you shouldn't, so you need to detect unforeseen, non-local attributes with the hasOwnProperty method.
In addition to non-enumerable attributes, you'll encounter a tougher problem when you try to copy objects that have hidden properties. For example, prototype is a hidden property of a function. Also, an object's prototype is referenced with the attribute __proto__, which is also hidden, and will not be copied by a for/in loop iterating over the source object's attributes. I think __proto__ might be specific to Firefox's JavaScript interpreter and it may be something different in other browsers, but you get the picture. Not everything is enumerable. You can copy a hidden attribute if you know its name, but I don't know of any way to discover it automatically.
Yet another snag in the quest for an elegant solution is the problem of setting up the prototype inheritance correctly. If your source object's prototype is Object, then simply creating a new general object with {} will work, but if the source's prototype is some descendant of Object, then you are going to be missing the additional members from that prototype which you skipped using the hasOwnProperty filter, or which were in the prototype, but weren't 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 over the attributes, but then you still will not get non-enumerable attributes. For example, a Date object stores its data as 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 will be 5 seconds behind that of d2. A way to make one Date the same as another is by calling the setTime method, but that is specific to the Date class. I don't think there is a bullet-proof general solution to this problem, though I would be happy to be wrong!
When I had to implement general deep copying I ended up compromising by assuming that I would only need to copy a plain Object, Array, Date, String, Number, or Boolean. The last 3 types are immutable, so I could perform a shallow copy and not worry about it changing. I further assumed that any elements contained in Object or Array would also be one of the 6 simple types in that list. This can be accomplished with code like 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."); }The above function will work adequately for the 6 simple types I mentioned, as long as the data in the objects and arrays form a tree structure. That is, there isn't more than one reference to the same data in the object. 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 will not be able to handle any JavaScript object, but it may be sufficient for many purposes as long as you don't assume that it will just work for anything you throw at it.
发布评论