JavaScript中的克隆对象[重复]
Posted
技术标签:
【中文标题】JavaScript中的克隆对象[重复]【英文标题】:Clone object in JavaScript [duplicate] 【发布时间】:2014-07-08 15:35:01 【问题描述】:考虑以下代码或检查此fiddle。
var obj =
name: "abc",
age: 20
var objTwo;
console.log(obj.age);
objTwo = obj;
objTwo.age = 10;
console.log(obj.age);
我创建了一个名为 obj 的对象,它有两个属性。现在我将 obj 分配给另一个名为 objTwo 的对象。现在我更新了 objTwo 中的一个属性。同样的变化也反映在 obj 上。 如何在不创建引用的情况下将值从一个对象分配给另一个对象?
【问题讨论】:
"assign without reference" 在 javascript 和类似语言中被称为 "clone"。有关可能的实现,请参阅this question。 @georg:谢谢。克隆是正确的词。我错过了 @georg——接受的答案没有一个特别好的答案,它基本上说“使用 jQuery”。 @RobG:我现在对人们使用 Jquery 来做一些普通的 javascript 可以做的事情有疑问。对库的过多依赖确实不利于编程思维。 只要使用我这里提到的jQuery扩展方法:***.com/a/23759239/3622881 【参考方案1】:这不会通过引用分配
<script>
var obj =
name: 'abc',
age: '30'
;
var objTwo = ;
for( var i in obj )
objTwo[i] = obj[i];
</script>
查看fiddle
【讨论】:
你应该有一个hasOwnProperty 过滤器(或使用Object.keys),上面也会复制可枚举的继承属性。 感谢您提供新信息..使我的编码更好..@RobG @RobG:但对于本例中的普通对象,这不是必需的。只有当你确实实现了一个可能曾经用于克隆花哨实例的通用函数时,你才会处理它。 @Bergi — 我认为这只是一个示例,我认为 OP 将很快转向更复杂的场景。复制或克隆对象只能在一定的参数范围内完成,没有通用的解决方案。与 RGraham 的评论相关的问题的答案可能涵盖了它,但没有一个单一的答案能真正起到作用。【参考方案2】:我会使用 jQuery 来做到这一点:
var obj1 =
name: "abc",
age: 20
console.log(obj1);
var obj2 = $.extend(, obj1, );
console.log(obj2);
obj2.age = 1;
console.log(obj2);
console.log(obj1);
【讨论】:
【参考方案3】:如果你只需要克隆简单的对象,只需这样做
JSON.parse (JSON.stringify (obj))
足够了。
但这显然不适用于所有情况,因为JSON.stringify
无法处理循环引用并剥离函数。
因此,如果您想超越这一点,事情会变得更加复杂,您必须依赖一些实用程序库或需要实现自己的深度克隆方法。
这是一个示例实现,它期望克隆一定程度的深度。
(function (Object, Array)
function cloneObject(deep, scope, clonedScope)
var type = typeof this,
clone = ,
isCR = -1;
deep = Number(deep) || 0;
scope = scope || [];
clonedScope = clonedScope || [];
if (!Array.isArray(scope) || !Array.isArray(clonedScope) || clonedScope.length !== scope.length)
throw new TypeError("Unexpected input");
//If we find a primitive, we reeturn its value.
if (type !== "object")
return this.valueOf();
scope.push(this);
clonedScope.push(clone);
if (0 === deep) //If we reached the recursion limit, we can perform a shallow copy
for (var prop in this)
clone[prop] = this[prop];
else //Otherwise we need to make some checks first.
for (var prop in this)
if ((isCR = scope.indexOf(this[prop])) > -1) //If we find a circular reference, we want create a new circular reference to the cloned version.
clone[prop] = clonedScope[isCR];
else if (typeof this[prop] !== "undefined" && this[prop] !== null) //Otherwise continue cloning.
clone[prop] = (typeof this[prop] !== "object" ? this[prop] : this[prop].clone(deep - 1, scope, clonedScope)); //If we find a non object, we can directly assign it. Otherwise we need to recursively call the clone function, counting down the limit, and injecting the scopeArrays, to find circular references.
else //If the property is undefined or null, assign it as such.
clone[prop] = this[prop];
scope.pop(); //If we leave a recursion leve, we remove the current object from the list.
clonedScope.pop();
return clone;
function cloneArray(deep, scope, clonedScope)
var clone = [];
deep = Number(deep) || 0;
scope = scope || [];
clonedScope = clonedScope || [];
if (!Array.isArray(scope) || !Array.isArray(clonedScope) || clonedScope.length !== scope.length)
throw new TypeError("Unexpected input");
scope.push(this);
clonedScope.push(this);
if (0 === deep) clone = this.concat();
else this.forEach(function (e)
if ((isCR = scope.indexOf(e)) > -1)
clone.push(clonedScope[isCR]);
else if (typeof e !== "undefined" && e !== null)
clone.push((typeof e !== "object" ? e : e.clone(deep - 1, scope, clonedScope)));
else
clone.push(e);
);
scope.pop();
clonedScope.pop();
return clone;
Object.defineProperty(Object.prototype, "clone",
enumerable: false,
value: cloneObject
);
Object.defineProperty(Array.prototype, "clone",
enumerable: false,
value: cloneArray
);
)(Object, Array);
请注意,扩展内置原型通常是不受欢迎的,但我决定这样做以避免额外的类型检查,并将数组和对象的逻辑拆分得更多。这可以很容易地重构为普通函数
一些测试来检查我们确实有新的参考。
var first =
a:
b: "b",
c:
,
b: "asd",
c: [],
d: undefined,
e: null,
f: function a() //functions keep their original reference..
;
first.a.c.a = first.a; //Circular object reference
first.c.push(first.c); //Circular array reference
var second = first.clone(Infinity);
console.log(second, second.a === second.a.c.a, first.a !== second.a.c.a); //..., true, true.
可能还有很大的改进空间,我特别不喜欢 scope 和 clonedScope 的注入方式。如果有人对查找和重新附加循环引用有更好的想法,我很乐意更新答案
这里也是Fiddle。
【讨论】:
【参考方案4】:var obj, objTwo;
obj =
name: "abc",
age: 20
console.log(obj.age);
objTwo = copy(obj);
objTwo.age = 10;
console.log(obj.age);
function copy (obj)
var key, rtn = Object.create(Object.getPrototypeOf(obj));
for (key in obj)
if (obj.hasOwnProperty(key))
rtn[key] = obj[key];
return rtn;
在 javascript 中,一切都是通过引用传递的。修改“泄漏”到原始函数属性(变量)的原因是因为您正在修改对象的属性,而不是对象(引用)本身。将对象复制到另一个变量,而不是重新分配它。
题外话:
在 javascript 中,一切都是通过引用传递的。 显然有点争议;这就是为什么我要添加这个附录。如果我错了,请随时纠正我。
Is JavaScript a pass-by-reference or pass-by-value language?上面的回答说得最清楚:
相反,情况是传入的item是按值传递的。 但是按值传递的项目本身是一个引用。
所以我的措辞令人困惑,但是如果您还记得每个运算符都返回一个引用,并且记住每个赋值和参数传递都只是复制运算符(和值文字)返回的那些引用,那么通过引用传递是有道理的。
链接的最佳答案有完整(正确)的解释。
【讨论】:
Unhandled Error: Object.defineProperties: property descriptor is not object
@Bergi 感谢您指出这一点。我犯了愚蠢的错误。 @ Jack_of_All_Trades 可能是真的,一些建设性的论点将不胜感激。我忽略了你所说的一切都是大写字母,我不感兴趣。
@KemHeyndels:对此我深表歉意。这是为了幽默。再次为此感到抱歉。这是我附加的链接,它可能有助于纠正我的无知行为。Javascript 通过原始类型的值和对对象类型的引用传递。链接可能会提供更多信息。 snook.ca/archives/javascript/javascript_pass
你为什么现在做Object.create(null)
?使用相同的原型完全没问题。
@Bergi 这只是我个人的偏见(很少使用原型)。我会把它带回来,反正可能更可靠。【参考方案5】:
好的,这可能是一个奇怪的解决方案,但它是纯 JavaScript。如果你想克隆一些东西,我会用“深拷贝”这个词,你可以像这样使用 JSON:
var obj =
name: "abc",
age: 20
new_object=JSON.parse(JSON.stringify(obj));
现在,您已经克隆了对象 obj。
另一种解决方案是这样的:
var new_obj=;
for( new_prop in obj)
if (obj.hasOwnProperty(new_prop))
new_obj[new_prop]=obj[new_prop]
【讨论】:
这可能会产生不必要的副作用,例如将 Date 对象转换为字符串。 反对者,想解释一下原因吗? 我没有投反对票,但我认为这是因为它不是一个好的解决方案。它还将忽略值为函数的属性以及值为 undefined. 的属性以上是关于JavaScript中的克隆对象[重复]的主要内容,如果未能解决你的问题,请参考以下文章