简体   繁体   English

在javascript中完全克隆一个对象

[英]Exactly clone an object in javascript

I tried to exactly clone an object in javascript. 我试图在javascript中完全克隆一个对象。 I know the following solution using jquery: 我使用jquery知道以下解决方案:

var newObject = jQuery.extend({}, oldObject);
// Or
var newObject = jQuery.extend(true, {}, oldObject);

but the problem with that is, that the objects type gets lost: 但问题是,对象类型丢失了:

var MyClass = function(param1, param2) {
    alert(param1.a + param2.a);
};
var myObj = new MyClass({a: 1},{a: 2});
var myObjClone = jQuery.extend(true, {}, myObj);
alert(myObj instanceof MyClass);      // => true
alert(myObjClone instanceof MyClass); // => false

Is there any solution to get true on the second alert? 是否有任何解决方案可以在第二个警报上实现?

jQuery.extend is not expecting you to use the instanceof operator. jQuery.extend不希望你使用instanceof运算符。 It is doing a gloriously complicated copy, and not a true clone. 它正在做一个光荣复杂的副本,而不是一个真正的克隆。 Looping through the elements is not enough. 循环使用元素是不够的。 Also, calling the constructor isn't best cause you'll loose your arguments. 此外,调用构造函数不是最好的,因为你将失去你的参数。 Try this: 试试这个:

var MyClass = function(param1, param2) {
    alert(param1.a + param2.a);
    this.p1 = param1;
    this.p2 = param2;
};

function Clone() { }
function clone(obj) {
    Clone.prototype = obj;
    return new Clone();
}

var myObj = new MyClass({a: 1},{a: 2});
var myObjClone = clone(myObj);
alert(myObj instanceof MyClass);      // => true
alert(myObjClone instanceof MyClass); // => true
console.log(myObj);       //note they are
console.log(myObjClone)   //exactly the same

Be aware that since your prototype now points back to the origional (myObj), any changes to myObj will reflect in myObjClone. 请注意,由于您的原型现在指向原始(myObj),myObj的任何更改都将反映在myObjClone中。 Javascript's prototypal inheritance is kinda tricky. Javascript的原型继承有点棘手。 You need to be sure that your new object has the correct prototype, and hence the correct constructor. 您需要确保新对象具有正确的原型,因此需要正确的构造函数。

Admitadly, Javascript makes my head hurt. Admitadly,Javascript让我头疼。 Still, I think I'm reading this right, from the ECMAScript language spec : 不过,我认为我正在从ECMAScript语言规范中读到这个:

13.2.2 [[Construct]] 13.2.2 [[构造]]
When the [[Construct]] internal method for a Function object F is called with a possibly empty list of arguments, the following steps are taken: 当使用可能为空的参数列表调用Function对象F的[[Construct]]内部方法时,将执行以下步骤:

  1. Let obj be a newly created native ECMAScript object. 让obj成为新创建的本机ECMAScript对象。
  2. Set all the internal methods of obj as specified in 8.12. 按照8.12中的规定设置obj的所有内部方法。
  3. Set the [[Class]] internal property of obj to "Object". 将obj的[[Class]]内部属性设置为“Object”。
  4. Set the [[Extensible]] internal property of obj to true. 将obj的[[Extensible]]内部属性设置为true。
  5. Let proto be the value of calling the [[Get]] internal property of F with argument >"prototype". 让proto成为使用参数>“prototype”调用F的[[Get]]内部属性的值。
  6. If Type(proto) is Object, set the [[Prototype]] internal property of obj to proto. 如果Type(proto)是Object,则将obj的[[Prototype]]内部属性设置为proto。
  7. If Type(proto) is not Object, set the [[Prototype]] internal property of obj to the >standard built-in Object prototype object as described in 15.2.4. 如果Type(proto)不是Object,则将obj的[[Prototype]]内部属性设置为>标准内置Object原型对象,如15.2.4中所述。
  8. Let result be the result of calling the [[Call]] internal property of F, providing >obj as the this value and providing the argument list passed into [[Construct]] as args. 令result为调用F的[[Call]]内部属性的结果,提供> obj作为此值,并将传递给[[Construct]]的参数列表作为args。
  9. If Type(result) is Object then return result. 如果Type(result)是Object,则返回结果。
  10. Return obj. 返回obj。

This person seems to understand the concept much better than I do. 这个人似乎比我更了解这个概念。 K, I'm going back to java now where I swim more than I sink :) . K,我现在回到java,我游泳比我下沉:)。

Have you considered using the clone function suggested here ? 您是否考虑过使用此处建议的克隆功能

function clone(obj){
    if(obj == null || typeof(obj) != 'object'){
        return obj;
    }

    var temp = new obj.constructor();
    for(var key in obj){
        temp[key] = clone(obj[key]);
    }
    return temp;
}

var MyClass = function(param1, param2) {};
var myObj = new MyClass(1,2);
var myObjClone = clone(myObj);
alert(myObj instanceof MyClass);      // => true
alert(myObjClone instanceof MyClass); // => true

After taking inspiration from some answers I've found on StackOverflow, I've come up with a function that is quite flexible, and still works when the object or any of its sub-objects has a constructor with required parameters (thanks to Object.create). 从我在StackOverflow上找到的一些答案中汲取灵感之后,我想出了一个非常灵活的函数,当对象或其任何子对象具有带有必需参数的构造函数时仍然有效(感谢Object。创建)。

(Thanks to Justin McCandless this now supports cyclic references as well.) (感谢Justin McCandless,现在也支持循环引用。)

//If Object.create isn't already defined, we just do the simple shim, without the second argument,
//since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://stackoverflow.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
    if(src === null || typeof(src) !== 'object'){
        return src;
    }

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Date
    if(src instanceof Date){
        return new Date(src.getTime());
    }
    //RegExp
    if(src instanceof RegExp){
        return new RegExp(src);
    }
    //DOM Element
    if(src.nodeType && typeof src.cloneNode == 'function'){
        return src.cloneNode(true);
    }

    // Initialize the visited objects arrays if needed.
    // This is used to detect cyclic references.
    if (_visited === undefined){
        _visited = [];
        _copiesVisited = [];
    }

    // Check if this object has already been visited
    var i, len = _visited.length;
    for (i = 0; i < len; i++) {
        // If so, get the copy we already made
        if (src === _visited[i]) {
            return _copiesVisited[i];
        }
    }

    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice() by itself would soft clone
        var ret = src.slice();

        //add it to the visited array
        _visited.push(src);
        _copiesVisited.push(ret);

        var i = ret.length;
        while (i--) {
            ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
        }
        return ret;
    }

    //If we've reached here, we have a regular object

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var dest = object_create(proto);

    //add this object to the visited array
    _visited.push(src);
    _copiesVisited.push(dest);

    for (var key in src) {
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        dest[key] = deepCopy(src[key], _visited, _copiesVisited);
    }
    return dest;
}

This function is part of my simpleOO library; 这个函数是我的simpleOO库的一部分; any bug fixes or enhancements will be made there (feel free to open an issue on github if you discover some bug). 任何错误修复或增强将在那里(如果你发现一些错误,随时在github上打开一个问题)。

function clone( obj ) {  
    var target = new obj.constructor();  
    for ( var key in target ) { delete target[key]; }  
    return $.extend( true, target, obj );  
}  

$.extend can't copy all the internal properties that are not visible (some are visible in firefox), but if obj.constructor is correct, and won't fault without args, the internal properties can be set with new obj.constructor() . $ .extend无法复制所有不可见的内部属性(有些在firefox中可见),但是如果obj.constructor是正确的,并且没有args就不会obj.constructor ,可以使用new obj.constructor()设置内部属性new obj.constructor() If you do inheritance with something like Derived.prototype = new Base() , you also would need to follow that with Derived.prototype.constructor = Derived to get the constructor right. 如果使用Derived.prototype = new Base()进行继承,则还需要使用Derived.prototype.constructor = Derived进行继承,以使构造函数正确。

You could do $.extend( true, new obj.constructor(), obj ) , but it's possible the constructor creates properties that were later deleted -- even if you could get the constructor args right -- that's why the properties have to be deleted before doing the extend. 你可以做$.extend( true, new obj.constructor(), obj ) ,但是构造函数可能会创建后来被删除的属性 - 即使你可以使构造函数args正确 - 这就是属性必须是在进行扩展之前删除。 It doesn't matter that the constructor args are wrong since the effects of the original constructor args -- plus everything else that has happened to the object since then -- are in the object we're cloning. 构造函数args是错误的并不重要,因为原始构造函数args的效果 - 加上从那时起发生在对象上的所有其他东西 - 都在我们正在克隆的对象中。

The problem is that you are passing in a new object to copy to '{}'. 问题是您传入的新对象要复制到“{}”。 This is why you lose your type. 这就是你失去类型的原因。 I found that if you wrap the real object before passing it in and unwrap the copied object afterwards, extend will preserve the type as expected. 我发现如果你在传入真实对象之前将其包装起来并在之后解包复制的对象,则extend将按预期保留该类型。

function clone(obj)
{
    var wrappedObj = { inner: obj };
    var newObject = jQuery.extend(true, {}, wrappedObj);
    newObject = newObject.inner;
    return newObject;
}

In Firefox you could write: 在Firefox中你可以写:

Object.prototype.clone = function() {
  return eval(uneval(this));
}

Can be used like: 可以像:

object1 = object2.clone();

Answer was found here : source 答案在这里找到: 来源

But it's just Firefox magic. 但这只是Firefox的魔力。 Other browsers might crash here. 其他浏览器可能会崩溃。

Here's an alternate solution that doesn't exactly copy or clone anything, but should give the desired results. 这是一个替代解决方案,不能完全复制或克隆任何东西,但应该给出所需的结果。

var myObj = new MyClass({a: 1},{a: 2});
var myObjCreator = MyClass.bind(this, {a: 1},{a: 2});
var myObjClone = new myObjCreator();

This uses Javascript's bind function to create an object that automatically passes in the given parameters to the MyClass constructor. 这使用Javascript的bind函数创建一个对象,该对象自动将给定的参数传递给MyClass构造函数。

I had similar requirements as the OP and this worked for me so I thought I'd post it, though I realize some people might need a true deep copy on a modified object and this won't fit the bill. 我有与OP相似的要求,这对我有用,所以我想我会发布它,虽然我发现有些人可能需要在修改后的对象上进行真正的深层复制,这不符合要求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM