我如何正确克隆一个JavaScript对象?
我有一个对象, x
。 我想将它复制为对象y
,以便对y
更改不会修改x
。 我意识到复制从内置JavaScript对象派生的对象将导致额外的不需要的属性。 这不是问题,因为我正在复制我自己的一个字面构造对象。
我如何正确克隆一个JavaScript对象?
更新了答案
只需使用Object.assign(),就像这里所建议的那样
过时的答案
为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 cyclicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
cyclicGraph["right"] = cyclicGraph;
它将无法处理任何JavaScript对象,但只要您不假定它只适用于任何对象,就可能用于许多目的。
使用jQuery,您可以使用扩展进行浅拷贝 :
var copiedObject = jQuery.extend({}, originalObject)
随后对copiedObject的更改不会影响originalObject,反之亦然。
或者做一个深层复制 :
var copiedObject = jQuery.extend(true, {}, originalObject)
如果你不使用你的对象中的函数,一个非常简单的一行就可以如下所示:
var cloneOfA = JSON.parse(JSON.stringify(a));
这适用于包含对象,数组,字符串,布尔值和数字的所有类型的对象。
另请参阅本文关于浏览器的结构化克隆算法,用于向工作人员发布消息和向工作人员发布消息时使用。 它还包含深层克隆功能。
链接地址: http://www.djcxy.com/p/621.html上一篇: How do I correctly clone a JavaScript object?
下一篇: Add a column with a default value to an existing table in SQL Server