Дублировать объект в javascript

Я вижу два способа дублирования объектов

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 ответов


первый не создает копию, а просто копирует ссылку, поэтому a и b указывают на тот же объект после операции.

во втором случае, однако, каждый атрибут копируется отдельно, таким образом, создавая "реальную" копию объекта в a (пока в свойствах есть только примитивные типы, иначе у вас будет та же проблема на более глубоком уровне).

Итак, в первом случае, если вы измените b.c затем a.c также изменится, в то время как во втором случае-нет.


в первой версии вы не дублируете/клонируете объект, вы просто делаете дополнительную ссылку на него:

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;
}

как заявили здесь другие: первое назначение, назначает новую ссылку на существующий объект, второе выполняет мелкая копия.
By мелкий Я имею в виду: будет скопирован только базовый объект, рекурсии нет:

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)


первые копии ссылки и не дублирует объект, второй создает новую ссылку, а затем копирует элементы (которые, в свою очередь, если они являются ссылками, будут просто копировать ссылки).

возможно, вы захотите посмотреть на это другое так -равно ли Javascript знак ссылочных объектов или клонирует их?

Это не вопрос эффективности, это, в конечном счете, о правильности. Если вам нужно поделиться ссылка на объект между различными блоками кода (например, чтобы несколько частей кода могли использовать один и тот же объект) - тогда вы просто полагаетесь на то, что javascript проходит по ссылке.

Если, однако, вам нужно скопировать объект между методами-тогда вы можете использовать свой простой пример во втором блоке кода (если у ваших объектов нет других "объектов" в них), иначе вам может потребоваться реализовать глубокий клон (см. как глубоко клонировать в в JavaScript, и обратите внимание на ответ(ы) там - это не тривиальное дело).