Способы клонирования объектов js

Копирование объектов и ссылки

Одним из фундаментальных отличий объектов от примитивных типов данных является то, что они хранятся и копируются «по ссылке».

Примитивные типы: строки, числа, логические значения – присваиваются и копируются «по значению».

В результате мы имеем две независимые переменные, каждая из которых хранит строку «Привет!» .

Объекты ведут себя иначе.

Переменная хранит не сам объект, а его «адрес в памяти», другими словами «ссылку» на него.

Сам объект хранится где-то в памяти. А в переменной user лежит «ссылка» на эту область памяти.

Когда переменная объекта копируется – копируется ссылка, сам же объект не дублируется.

Если мы представляем объект как ящик, то переменная – это ключ к нему. Копирование переменной дублирует ключ, но не сам ящик.

Теперь у нас есть две переменные, каждая из которых содержит ссылку на один и тот же объект:

Мы можем использовать любую из переменных для доступа к ящику и изменения его содержимого:

Приведённый выше пример демонстрирует, что объект только один. Как если бы у нас был один ящик с двумя ключами и мы использовали один из них ( admin ), чтобы войти в него и что-то изменить, а затем, открыв ящик другим ключом ( user ), мы бы увидели эти изменения.

Сравнение по ссылке

Операторы равенства == и строгого равенства === для объектов работают одинаково.

Два объекта равны только в том случае, если это один и тот же объект.

В примере ниже две переменные ссылаются на один и тот же объект, поэтому они равны друг другу:

В другом примере два разных объекта не равны, хотя оба пусты:

Для сравнений типа obj1 > obj2 или для сравнения с примитивом obj == 5 объекты преобразуются в примитивы. Мы скоро изучим, как работают такие преобразования объектов, но, по правде говоря, сравнения такого рода необходимы очень редко и обычно являются результатом ошибки программиста.

Клонирование и объединение объектов, Object.assign

Таким образом, при копировании переменной с объектом создаётся ещё одна ссылка на тот же самый объект.

Но что, если нам всё же нужно дублировать объект? Создать независимую копию, клон?

Это выполнимо, но немного сложно, так как в JavaScript нет встроенного метода для этого. На самом деле, такая нужда возникает редко. В большинстве случаев нам достаточно копирования по ссылке.

Но если мы действительно этого хотим, то нам нужно создавать новый объект и повторять структуру дублируемого объекта, перебирая его свойства и копируя их.

Кроме того, для этих целей мы можем использовать метод Object.assign.

  • Первый аргумент dest — целевой объект.
  • Остальные аргументы src1, . srcN (может быть столько, сколько нужно) являются исходными объектами
  • Метод копирует свойства всех исходных объектов src1, . srcN в целевой объект dest . То есть, свойства всех перечисленных объектов, начиная со второго, копируются в первый объект.
  • Возвращает объект dest .
Читайте также:  Способ производства какие бывают

Например, объединим несколько объектов в один:

Если принимающий объект ( user ) уже имеет свойство с таким именем, оно будет перезаписано:

Мы также можем использовать Object.assign для замены for..in на простое клонирование:

Этот метод скопирует все свойства объекта user в пустой объект и возвратит его.

Вложенное клонирование

До сих пор мы предполагали, что все свойства объекта user хранят примитивные значения. Но свойства могут быть ссылками на другие объекты. Что с ними делать?

Например, есть объект:

Теперь при клонировании недостаточно просто скопировать clone.sizes = user.sizes , поскольку user.sizes – это объект, он будет скопирован по ссылке. А значит объекты clone и user в своих свойствах sizes будут ссылаться на один и тот же объект:

Чтобы исправить это, мы должны в цикле клонирования делать проверку, не является ли значение userСпособы клонирования объектов js объектом, и если это так – скопировать и его структуру тоже. Это называется «глубокое клонирование».

Мы можем реализовать глубокое клонирование, используя рекурсию. Или, чтобы не изобретать велосипед, использовать готовую реализацию — метод _.cloneDeep(obj) из JavaScript-библиотеки lodash.

Итого

Объекты присваиваются и копируются по ссылке. Другими словами, переменная хранит не «значение объекта», а «ссылку» (адрес в памяти) на это значение. Поэтому копирование такой переменной или передача её в качестве аргумента функции приводит к копированию этой ссылки, а не самого объекта.

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

Источник

Копирование объектов в JavaScript

Sep 9, 2019 · 7 min read

Объекты это основа JavaScript’а. Сам по себе, объект это набор данных, где каждая единица информации является связью между ключом (или именем) и значением. Почти все объекты в JavaScript это экземпляры Object, который стоит на вершине цепочки прототипов.

👉 Мой Твиттер — там много из мира фронтенда, да и вообще поговорим🖖. Подписывайтесь, будет интересно: ) ✈️

Введение

Как вы уже наверное знаете, оператор назначения не создаёт копию объекта, а только делает отсылку к нему, давайте посмотрим на следующий код:

Тут переменная obj это контейнер для нового объекта. Переменная copy указывает на этот же объект и является своеобразной отсылкой к нему. Так что, просто напросто, объект < a: 1, b: 2, >как бы говорит нам: Есть два способа получить ко мне доступ. Вы можете получить его через переменную obj или через переменную copy , в обоих случаях получите возможность работать со мной и всё, что бы вы не делали этими способами, отобразится на мне.

Сейчас много говорят про иммутабельность и поверьте, вам стоит прислушаться к этим разговорам. Описанный выше метод исключает любую форму иммутабельности и может привести к ошибкам при использовании оригинального объекта при задействовании уже другой части вашего кода.

Простой и наивный способ копирования объектов

Простой способ копирования объектов заключается в том, что пройтись циклом по оригинальному объекту и скопировать каждое свойство по очереди. Давайте взглянем на такой код:

Проблемы при таком методе:

objCopy объект имеет другой метод Object.prototype , отличающийся от mainObj прототип метода, который не совсем то, что нам нужно. Нам нужна точная копия оригинального объекта.

Дескрипторы свойств не скопированы. Перезаписываемый дескриптор со значением выставленным на false , будет выставлен на true в объекте objCopy .

Читайте также:  Как пить табекс способ применения

Код выше копирует только перечисляемые свойства объекта mainObj .

Если одно из свойств в оригинальном объекте является тоже объектом, то оно будет расшарено и совместно использоваться, как копией, так и оригинальным объектом, заставляя ссылаться соответствующие свойства на один и тот же объект.

Поверхностное копирование объектов

Поверхностно скопированным объектом, можно называть объект, когда исходные top-level (самые первые в иерархии объекта) свойства скопированы без какой-либо отсылки и существует исходное свойство, чьё значение это объект и он скопирован уже как отсылка. А если исходное значение это отсылка к объекту, то он копирует только отсылку к значению целевого объекта.

Поверхностная копия скопирует только top-level свойства, но вложенные объекты будут использоваться между оригиналом, так и копией.

Используем метод Object.assign()

Это метод используется для копирования значений всех собственных перечисляемых свойств из одного или нескольких исходных объектов в один целевой объект.

Отлично, пока что все работает как надо. Мы сделали копию obj . Давайте посмотрим на наличие иммутабельности:

В коде выше, мы изменили значение свойства b в objCopy объекте на 89 и когда мы вывели измененный objCopy объект в консоль, изменения применились только к objCopy . Последняя строчка кода проверяет то, что объект obj до сих пор не тронут и не изменён. Это подразумевает то, что мы успешно создали копию исходного объекта без каких либо отсылок к нему.

Подводные камни Object.assign()

Не так быстро! Пока мы успешно создали копию и все вроде бы отлично работает, вспомните то, что мы обсуждали поверхностное копирование? Давайте посмотрим на пример ниже:

Что? Почему obj.b.c = 30?

Отлично, вот мы попались в ловушку с Object.assign() . Object.assign делает только поверхностную копию. А newObj.b и obj.b вместе отсылаются к одному и тому объекту, так как отдельные копии не создавались, а была сделана отсылка к объекту. Любое изменение сделанное на любом свойстве объекта, применится ко всем отсылкам использующим объект. Как это исправить? Читаем дальше, там будет исправление этой проблемы.

Обратите внимание: свойства в цепочке прототипов и неперечисляемые свойства не могут быть скопированы. Как тут:

someObj находится в цепочке прототипов obj , поэтому он не скопируется.

Свойство b это неперечисляемое свойство.

Свойство c включает в себя дескриптор перечисляемого свойства, что позволяет ему быть скопированным.

Глубокое копирование объектов

Глубокая копия продублирует каждый объект на пути копирования. Оригинал и скопированный объект не будут иметь ничего общего и совместно использоваться не будут, в общем, это будет копия оригинала. А вот и решение проблемы с который мы столкнулись при использовании Object.assign() . Давайте посмотрим.

Использование JSON.parse(JSON.stringify(object));

Этот подход решает предыдущую проблему. Теперь newObj.b это копия, а не отсылка к оригинальному объекту! Вот так делается глубокое копирование объектов. Пример:

Небольшая загвоздка

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

Копируем методы объекта

Метод это свойство объекта, которое является функцией. Пока что, в примерах, мы не копировали объект с методом. Давайте попробуем это сделать с помощью уже изученных подходов!

Результат показывает нам то, что Object.assign() можно использовать для копирования методов, а JSON.parse(JSON.stringify(obj)) , к сожалению, нет.

Копирование циклических объектов

Циклические объекты — это объекты, у которых есть свойства, ссылающиеся сами на себя. Давайте применим уже известные методы копирования для создания копий циклических объектов и посмотрим, как они сейчас сработают.

Читайте также:  Gemene пилинг с фруктовыми кислотами способ применения

Источник

3 способа клонирования объектов в JavaScript

Nov 19, 2019 · 4 min read

Поскольку объекты в #JavaScript являются ссылочными значениями, их нельзя просто скопировать с помощью = . Но не беспокойтесь, существует 3 способа клонирования объекта 👍.

Объекты — это ссылочные типы

Почему нельзя использовать = ? Посмотрим, что может произойти:

Оба объекта выдают одно и то же. На данный момент никаких проблем. Рассмотрим, что произойдет после редактирования второго объекта:

obj2 был изменен, однако изменения коснулись и obj . Причина заключается в том, что объекты являются ссылочными типами. Поэтому при использовании = , указатель копируется в область занимаемой памяти. Ссылочные типы не содержат значений, они являются указателем на значение в памяти.

Использование Spread

С помощью s p read можно клонировать объект. Обратите внимание, что копия будет неглубокой. На момент публикации этого руководства оператор spread для клонирования объектов находился на стадии 4, соответственно официально он не указан в спецификациях. Поэтому для того, чтобы его использовать, нужно выполнить компиляцию с Babel (или чем-то подобным).

Использование Object.assign

Object.assign , выпущенный официально, также создает неглубокую копию объекта.

Использование JSON

Этот способ предоставляет глубокую копию. Стоит упомянуть, что это быстрый и грязный способ глубокого клонирования объекта. В качестве более надежного решения рекомендуется использовать что-то вроде lodash.

Lodash DeepClone или JSON?

  • JSON.stringify/parse работает только с литералом Number, String и Object без функции или свойства Symbol.
  • deepClone работает со всеми типами, а функция и символ копируются по ссылке.

Глубокое или неглубокое клонирование?

При использовании spread для копирования объекта создается неглубокая копия. Если массив является вложенным или многомерным, этот способ не будет работать. Рассмотрим пример:

Таким образом, клонированный объект был изменен с добавлением city. В результате получаем:

Неглубокая копия предполагает копирование первого уровня и ссылается на более глубокие уровни.

Глубокая копия

Возьмем тот же пример, но применим глубокую копию с использованием JSON:

Глубокая копия является копией для вложенных объектов. Однако иногда достаточно использования неглубокой копии.

Производительность

К сожалению, на данный момент нельзя написать тестирование для spread, поскольку официально он не указан в спецификации. Однако результат показывает, что Object.assign намного быстрее, чем JSON . Тест на производительность можно найти здесь.

Object.assign и Spread

Стоит отметить, что Object.assign — это функция, которая модифицирует и возвращает целевой объект. В данном примере при использовании:

<> — это модифицируемый объект. В этой точке на целевой объект не ссылаются никакие переменные, но поскольку Object.assign возвращает целевой объект, то можно сохранить полученный присвоенный объект в переменную cloneFood . Данный пример можно изменить следующим образом:

Очевидно, что значение beef в объекте food неверно, поэтому нужно назначить правильное значение beef с помощью Object.assign . На самом деле мы не используем возвращаемое значение функции, а изменяем целевой объект, на который ссылаемся с помощью константы food .

С другой стороны, Spread — это оператор, который копирует свойства одного объекта в новый объект. При репликации приведенного выше примера с помощью spread для изменения переменной food.

. мы получаем ошибку в результате использования spread для создания новых объектов и, следовательно, присваиваем новый объект для food , который был объявлен с помощью const , что недопустимо. Поэтому можно либо объявить новую переменную для хранения нового объекта, как показано ниже:

либо объявить food с let или var , что позволит присвоить новый объект:

Источник

Оцените статью
Разные способы