繁体   English   中英

在javascript中完全克隆一个对象

[英]Exactly clone an object in javascript

我试图在javascript中完全克隆一个对象。 我使用jquery知道以下解决方案:

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

但问题是,对象类型丢失了:

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

是否有任何解决方案可以在第二个警报上实现?

jQuery.extend不希望你使用instanceof运算符。 它正在做一个光荣复杂的副本,而不是一个真正的克隆。 循环使用元素是不够的。 此外,调用构造函数不是最好的,因为你将失去你的参数。 试试这个:

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

请注意,由于您的原型现在指向原始(myObj),myObj的任何更改都将反映在myObjClone中。 Javascript的原型继承有点棘手。 您需要确保新对象具有正确的原型,因此需要正确的构造函数。

Admitadly,Javascript让我头疼。 不过,我认为我正在从ECMAScript语言规范中读到这个:

13.2.2 [[构造]]
当使用可能为空的参数列表调用Function对象F的[[Construct]]内部方法时,将执行以下步骤:

  1. 让obj成为新创建的本机ECMAScript对象。
  2. 按照8.12中的规定设置obj的所有内部方法。
  3. 将obj的[[Class]]内部属性设置为“Object”。
  4. 将obj的[[Extensible]]内部属性设置为true。
  5. 让proto成为使用参数>“prototype”调用F的[[Get]]内部属性的值。
  6. 如果Type(proto)是Object,则将obj的[[Prototype]]内部属性设置为proto。
  7. 如果Type(proto)不是Object,则将obj的[[Prototype]]内部属性设置为>标准内置Object原型对象,如15.2.4中所述。
  8. 令result为调用F的[[Call]]内部属性的结果,提供> obj作为此值,并将传递给[[Construct]]的参数列表作为args。
  9. 如果Type(result)是Object,则返回结果。
  10. 返回obj。

这个人似乎比我更了解这个概念。 K,我现在回到java,我游泳比我下沉:)。

您是否考虑过使用此处建议的克隆功能

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

从我在StackOverflow上找到的一些答案中汲取灵感之后,我想出了一个非常灵活的函数,当对象或其任何子对象具有带有必需参数的构造函数时仍然有效(感谢Object。创建)。

(感谢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;
}

这个函数是我的simpleOO库的一部分; 任何错误修复或增强将在那里(如果你发现一些错误,随时在github上打开一个问题)。

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

$ .extend无法复制所有不可见的内部属性(有些在firefox中可见),但是如果obj.constructor是正确的,并且没有args就不会obj.constructor ,可以使用new obj.constructor()设置内部属性new obj.constructor() 如果使用Derived.prototype = new Base()进行继承,则还需要使用Derived.prototype.constructor = Derived进行继承,以使构造函数正确。

你可以做$.extend( true, new obj.constructor(), obj ) ,但是构造函数可能会创建后来被删除的属性 - 即使你可以使构造函数args正确 - 这就是属性必须是在进行扩展之前删除。 构造函数args是错误的并不重要,因为原始构造函数args的效果 - 加上从那时起发生在对象上的所有其他东西 - 都在我们正在克隆的对象中。

问题是您传入的新对象要复制到“{}”。 这就是你失去类型的原因。 我发现如果你在传入真实对象之前将其包装起来并在之后解包复制的对象,则extend将按预期保留该类型。

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

在Firefox中你可以写:

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

可以像:

object1 = object2.clone();

答案在这里找到: 来源

但这只是Firefox的魔力。 其他浏览器可能会崩溃。

这是一个替代解决方案,不能完全复制或克隆任何东西,但应该给出所需的结果。

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

这使用Javascript的bind函数创建一个对象,该对象自动将给定的参数传递给MyClass构造函数。

我有与OP相似的要求,这对我有用,所以我想我会发布它,虽然我发现有些人可能需要在修改后的对象上进行真正的深层复制,这不符合要求。

暂无
暂无

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

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