Дублировать объект в 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, и обратите внимание на ответ(ы) там - это не тривиальное дело).