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中的克隆对象[重复]的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript克隆“类”实例[重复]

JavaScript克隆“类”实例[重复]

关于JavaScript对象深度克隆

Java中的克隆列表[重复]

26.JavaScript实现对象混合与对象浅度克隆和对象的深度克隆

java中的浅克隆和深克隆是啥