8

我看到了两种复制对象的方法

1.

var a={c:1}
var b=a;
alert(b.c);//alert 1

2.

var a={c:2};
var b={};
for (i in a)
{b[i]=a[i];} 
alert(b.c);//alert 1

第一个比第二个短,那么第二个例子的效率是多少?

4

4 回答 4

13

在第一个版本中,您不会复制/克隆对象,只需对其进行额外引用:

var a = { a: 1 };
var b = a;
b.a = 2;

console.log(a.a); // 2;

要克隆一个对象,有许多库可以为您做到这一点:

var b = $.extend({}, a); // Make a shallow clone (jQuery)
var b _.extend({}, a); // Make a shallow clone (underscore.js)

var b = $.extend(true, {}, a); // Make a deep clone (jQuery);

或者您可以在本地进行:
简单克隆:

var b = {};
var prop;

for (prop in a) {
    b[prop] = a[prop];
}

深度克隆功能的草稿:

function deepClone(obj) {
    var r;
    var i = 0,
    var len = obj.length;
    // string, number, boolean
    if (typeof obj !== "object") { 
        r = obj;
    }
    // Simple check for array
    else if ( len ) { 
        r = [];
        for ( ; i < len; i++ ) {
            r.push( deepClone(obj[i]) );
        }
    } 
    // Simple check for date
    else if ( obj.getTime ) { 
        r = new Date( +obj );
    }
    // Simple check for DOM node
    else if ( obj.nodeName ) { 
        r = obj;
    }
    // Object
    else { 
        r = {};
        for (i in obj) {
            r[i] = deepClone(obj[i]);
        }
    }

    return r;
}
于 2012-11-08T10:39:32.840 回答
8

第一个不创建副本,而只是复制引用,因此操作ab指向同一个对象。

然而,在第二种情况下,每个属性都被单独复制,从而在其中创建对象的“真实”副本a(只要属性中只有原始类型,否则在更深层次上会遇到同样的问题)。

因此,在第一种情况下,如果您更改,b.c那么a.c也会更改,而在第二种情况下则不会。

于 2012-11-08T10:39:00.750 回答
4

正如其他人在这里所说:第一个分配,为现有对象分配一个新引用,第二个执行浅拷贝
我的意思是:只会复制基础对象,没有递归:

var a = {some:'propery',
         another:{might:'be',
                  an: 'object, too'}
        };
var b = {};
for(var p in a)
{
    b[p] = a[p];
}
b.some = 'b\'s own property';
console.log(a.some);//property -> unaltered
console.log(b.some);//b's own property --> separate entities
b.another.might = 'foo';
console.log(a.another.might);//foo ==> b.another references a.another

为了解决这个问题,你会认为一个简单的递归函数就足够了:

var cloneObj = function(o)
{
    var p,r = {};
    for (p in o)
    {//omitting checks for functions, date objects and the like
        r[p] = (o[p] instanceof Object ? cloneObj(o[p]) : o[p]);
    }
};

但要厌倦循环引用

//assume a is the same object as above
a._myself = a;//<- a references itself

这将产生无休止的递归,也就是死锁场景,除非您针对这种情况添加检查:

var cloneObj = function(o)
{
    var p,r = {};
    for (p in o)
    {//Needs a lot more work, just a basic example of a recursive copy function
        switch(true)
        {
            case o[p] instanceof Function:
                r[p] = o[p];
            break;
            case o[p] instanceof Date:
                r[p] = new Date(o[p]);
            break;
            case o === o[p]:
            //simple circular references only
            //a.some.child.object.references = a; will still cause trouble
                r[p] = r;
            break;
            case o[p] instanceof Array:
                r[p] = o[p].slice(0);//copy arrays
            break;
            default:
                r[p] = o[p] instanceof Object ? cloneObj(o[p]) : o[p];
        }
    }
    return r;
};

现在,这非常冗长,并且在大多数情况下完全过分,如果您想要的是两个具有相同数据但可以独立更改的对象(即不要在内存中引用相同的对象),那么您只需要 1 行代码:

var a = {some:'propery',
         another:{might:'be',
                  an: 'object, too'}
        };
var b = JSON.parse(JSON.stringify(a));

底线:分配引用肯定更有效:它不需要第二次调用对象构造函数,也不需要任何常量的额外副本。
缺点是:您最终得到一个对象,并且可能会无意中更改/删除您认为在使用其他引用时仍然存在的东西(delete b.some;/*some time later*/a.some.replace(/p/g,'q');//<--error

于 2012-11-08T11:10:08.773 回答
3

第一个复制引用并且不复制对象,第二个创建一个新引用然后复制成员(反过来,如果它们是引用将只复制引用)。

您可能想查看其他 SO - Javascript 是否等于号引用对象或克隆它们?

这不是效率问题,最终是关于正确性的问题。如果您需要在不同的代码块之间共享对对象的引用(例如,以便多段代码可以共享同一个对象),那么您只需依赖 javascript 通过引用传递的事实。

但是,如果您需要在方法之间复制一个对象 - 那么您可以在第二个代码块中使用您的简单示例(如果您的对象中没有其他“对象”),否则您可能必须实现深度克隆(请参阅How to Deep clone in javascript,并注意那里的答案 - 这不是一件小事)。

于 2012-11-08T10:39:20.660 回答