В отличие от данных примитивных типов данные объектов копируются по ссылке. Что это значит? Рассмотрим следующий пример:
const tom = { name: "Tom"}; const bob = tom; // проверяем свойство name у обоих констант console.log(tom.name); // Tom console.log(bob.name); // Tom // меняем свойство name у константы bob bob.name = "Bob"; // повторно проверяем свойство name у обоих констант console.log("После изменения") console.log(tom.name); // Bob console.log(bob.name); // Bob
Вначале определяется обычный объект tom
с одним свойством name
. Затем присваиваем значение этого объекта константе bob
const bob = tom;
В данном случае константа bob
получае ссылку или условно говоря адрес константы tom
, поэтому после этого присвоения обе константы
по сути указывают на один и тот же объект в памяти. Соответственно изменения, произведенные через одну константу:
bob.name = "Bob";
Затронут и другую константу - tom
:
console.log(tom.name); // Bob\
Более того, добавим к объекту новое свойство через одну из констант:
const tom = { name: "Tom"}; const bob = tom; // добавляем константе bob новое свойство - age bob.age = 37; // и видим, что для tom тоже добавлено новое свойство console.log(tom.age); // 37
После добавления свойства age
константе bob
можно увидеть, что у константы tom
то же появилось это свойство, потому что
опять же обе константы представляют один и тот же объект.
Что же если мы хотим скопировать из свойства объекта, но при этом обе константы или переменных указывали бы на совершенно разные объекты, изменения одного из которых никак бы не затрагивали другой? В этом случае мы можем воспользоваться встроенным методом Object.assign().
Метод Object.assign() принимает два параметра:
Object.assign(target, ...sources)
Первый параметр - target
представляет объект, в который надо скопировать свойства. Второй параметр - ...sources
- набор объектов, из
которых надо скопировать свойства (то есть мы можем скопировать свойства сразу из нескольких объектов)
Возвращает метод объект target, в который скопированы свойства из объектов sources
.
Например:
const tom = { name: "Tom", age: 37}; const bob = Object.assign({}, tom); bob.name = "Bob"; bob.age = 41; console.log(`Объект tom. Name: ${tom.name} Age: ${tom.age}`); console.log(`Объект bob. Name: ${bob.name} Age: ${bob.age}`);
В данном случае вызов Object.assign({}, tom)
означает, что мы копируем данные из объекта tom
в пустой объект {}
.
Результатом этого копирования стал объект bob
. Причем это совсем другой объект, нежели tom
. И любые изменения с константой bob
здесь никак не затронут константу tom
.
Консольный вывод программы:
Объект tom. Name: Tom Age: 37 Объект bob. Name: Bob Age: 41
Подобным образом можно копировать данные из нескольких объектов:
const tom = { name: "Tom"}; const sam = { age: 37}; const person = { height: 170}; Object.assign(person, tom, sam); // копируем из tom и sam в person console.log(person); // {height: 170, name: "Tom", age: 37}
Здесь копируются все свойства из объектов tom и sam в объект person. В итоге после копирования объект person будет иметь три свойства.
Если объекты, из которых выполняется копирование, содержат одинаковые свойства, то свойства из последних объектов замещают свойства предыдущих:
const tom = { name: "Tom", age: 37}; const sam = { age: 45}; const person = { height: 170}; Object.assign(person, tom, sam); console.log(person); // {height: 170, name: "Tom", age: 45}
Здесь оба объекта - tom и sam содержат свойство age
, но в объекте person свойство age равно 45 - значение из объекта sam, потому что
копирование из объекта sam произодится в последнюю очередь.
Несмотря на то, что Object.assign()
прекрасно работает для простых объектов, но что будет, если свойство копируемого объекта также представляет объект:
const tom = { name: "Tom", company: {title: "Microsoft"}}; const bob = Object.assign({}, tom); bob.name = "Bob"; bob.company.title = "Google"; console.log(tom.name); // Tom console.log(tom.company.title); // Google
Здесь свойство company
объекта tom
представляет объект с одним свойством. И при копировании объект bob получит не копию значения tom.company
,
а ссылку на этот объект. Поэтому изменения bob.company
затронут и tom.company
.
spread-оператор ... позволяет разложить объект на различные пары свойство-значение
, которые можно передать другому объекту.
const tom = { name: "Tom", age: 37, company: "Google"}; const bob = {...tom} bob.name = "Bob"; console.log(tom); // {name: "Tom", age: 37, company: "Google"} console.log(bob); // {name: "Bob", age: 37, company: "Google"}
В данном случае объекту bob
передаются копии свойств объекта tom.
Если какие-то свойства нового объекта должны иметь другие значения (как в примере выше свойство name), то их можно указать в конце:
const tom = { name: "Tom", age: 37, company: "Google"}; const bob = {...tom, name: "Bob"}; console.log(bob); // {name: "Bob", age: 37, company: "Google"}
Как видно из предыдущего примера, обе константы после копирования представляют ссылки на разные объекты, и изменения одного из них никак не затронет другой объект.
Тем не менее если объекты содержат вложенные объекты, то эти вложенные объекты при копировании опять же по сути будут представлять ссылки на один и тот же объект:const tom = { name: "Tom", age: 37, company: {title: "Microsoft"}}; const bob = {...tom} bob.name = "Bob"; bob.company.title = "Google"; console.log(`${tom.name} - ${tom.company.title}`); // Tom - Google console.log(`${bob.name} - ${bob.company.title}`); // Bob - Google
Сравним два объекта с помощью стандартных операций сравнения и эквивалентности:
const tom = { name: "Tom"}; const bob = { name: "Bob"}; console.log(tom == bob); // false console.log(tom === bob); // false
Оба оператора в данном случае возвратят значение false
, то есть
объекты не равны. Причем даже если значения свойств объектов будет одинаковым, то мы все равно в обоих случаях получим false
const tom = { name: "Tom"}; const bob = { name: "Tom"}; console.log(tom == bob); // false console.log(tom === bob); // false
Однако, что будет, если обе константы (переменных) хранят ссылку на один и тот же объект:
const tom = { name: "Tom"}; const bob = tom; console.log(tom == bob); // true console.log(tom === bob); // true
В этом случае в обоих случаях мы получим true
, поскольку значения обоих констант равны, так как по сути это одно и то же значение.