Клонирование и объединение объектов, Object.assign
Таким образом, при копировании переменной с объектом создаётся ещё одна ссылка на тот же самый объект.
Но что, если нам всё же нужно дублировать объект? Создать независимую копию, клон?
Это выполнимо, но немного сложно, так как в JavaScript нет встроенного метода для этого. На самом деле, такая нужда возникает редко. В большинстве случаев нам достаточно копирования по ссылке.
Но если мы действительно этого хотим, то нам нужно создавать новый объект и повторять структуру дублируемого объекта, перебирая его свойства и копируя их.
Например так:
let user = {
name: "Иван",
age: 30
};
let clone = {}; // новый пустой объект
// скопируем все свойства user в него
for (let key in user) {
clone[key] = user[key];
}
// теперь в переменной clone находится абсолютно независимый клон объекта
clone.name = "Пётр"; // изменим в нём данные
alert( user.name ); // в оригинальном объекте значение свойства `name` осталось прежним – Иван.
Кроме того, для этих целей мы можем использовать метод Object.assign.
Синтаксис:
Object.assign(dest, [src1, src2, src3...])
- Первый аргумент
dest
— целевой объект. - Остальные аргументы
src1, ..., srcN
(может быть столько, сколько нужно)) являются исходными объектами - Метод копирует свойства всех исходных объектов
src1, ..., srcN
в целевой объектdest
. То есть, свойства всех перечисленных объектов, начиная со второго, копируются в первый объект. - Возвращает объект
dest
.
Например, объединим несколько объектов в один:
let user = { name: "Иван" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
// копируем все свойства из permissions1 и permissions2 в user
Object.assign(user, permissions1, permissions2);
// теперь user = { name: "Иван", canView: true, canEdit: true }
Если принимающий объект (user
) уже имеет свойство с таким именем, оно будет перезаписано:
let user = { name: "Иван" };
Object.assign(user, { name: "Пётр" });
alert(user.name); // теперь user = { name: "Пётр" }
Мы также можем использовать Object.assign
для замены for..in
на простое клонирование:
let user = {
name: "Иван",
age: 30
};
let clone = Object.assign({}, user);
Этот метод скопирует все свойства объекта user
в пустой объект и возвратит его.
Вложенное клонирование
До сих пор мы предполагали, что все свойства объекта user
хранят примитивные значения. Но свойства могут быть ссылками на другие объекты. Что с ними делать?
Например, есть объект:
let user = {
name: "Иван",
sizes: {
height: 182,
width: 50
}
};
alert( user.sizes.height ); // 182
Теперь при клонировании недостаточно просто скопировать clone.sizes = user.sizes
, поскольку user.sizes
– это объект, он будет скопирован по ссылке. А значит объекты clone
и user
в своих свойствах sizes
будут ссылаться на один и тот же объект:
let user = {
name: "Иван",
sizes: {
height: 182,
width: 50
}
};
let clone = Object.assign({}, user);
alert( user.sizes === clone.sizes ); // true, один и тот же объект
// user и clone обращаются к одному sizes
user.sizes.width++; // меняем свойство в одном объекте
alert(clone.sizes.width); // 51, видим результат в другом объекте
Чтобы исправить это, мы должны в цикле клонирования делать проверку, не является ли значение user[key]
объектом, и если это так – скопировать и его структуру тоже. Это называется «глубокое клонирование».
Мы можем реализовать глубокое клонирование, используя рекурсию. Или, чтобы не изобретать велосипед, использовать готовую реализацию — метод _.cloneDeep(obj) из JavaScript-библиотеки lodash.
Итого
Объекты присваиваются и копируются по ссылке. Другими словами, переменная хранит не «значение объекта», а «ссылку» (адрес в памяти) на это значение. Поэтому копирование такой переменной или передача её в качестве аргумента функции приводит к копированию этой ссылки, а не самого объекта.
Все операции с использованием скопированных ссылок (например, добавление или удаление свойств) выполняются с одним и тем же объектом.
Для «простого клонирования» объекта можно использовать Object.assign
. Необходимо помнить, что Object.assign
не делает глубокое клонирование объекта. Если внутри копируемого объекта есть свойство, значение которого не является примитивом, оно будет передано по ссылке. Для создания «настоящей копии» (полного клона объекта) можно воспользоваться методом из сторонней JavaScript-библиотеки _.cloneDeep(obj).
Практика
Проверка на пустоту
Напишите функцию isEmpty(obj), которая возвращает true,
если у объекта нет свойств, иначе false.
Сумма свойств объекта
Напишите код для суммирования всех свойств объекта и сохраните результат в переменной sum.