Вы знали, что теперь в JavaScript есть нативный способ делать глубокие копии объектов? Это стало возможным с помощью функции structuredClone, встроенной в среду выполнения JavaScript:
const calendarEvent = { title: "Builder.io Conf", date: new Date(123), attendees: ["Steve"] } // ? const copied = structuredClone(calendarEvent)
Вы заметили, что в этом примере мы скопировали не только объект, но и вложенный массив, и даже объект Date?
И код работает именно так, как мы и ожидали:
copied.attendees // ["Steve"] copied.date // Date: Wed Dec 31 1969 16:00:00 cocalendarEvent.attendees === copied.attendees // false
structuredClone может делать не только вышеперечисленное, но и также:
-
Клонировать бесконечно вложенные объекты и массивы.
-
Клонировать циклические ссылки.
-
Клонировать широкий спектр типов JavaScript, таких как:
Date,Set,Map,Error,RegExp,ArrayBuffer,Blob,File,ImageDataи многие другие. -
Передавать любые передаваемые объекты.
Это безумие даже будет работать так, как мы и ожидали:
const kitchenSink = { set: new Set([1, 3, 3]), map: new Map([[1, 2]]), regex: /foo/, deep: { array: [ new File(someBlobData, 'file.txt') ] }, error: new Error('Hello!') } kitchenSink.circular = kitchenSink // ✅ Выполнено полное глубокое копирование const clonedSink = structuredClone(kitchenSink)
Почему бы просто не сделать object spread?
Важным отметить, что мы говорим о глубоком копировании. Если же нужно просто выполнить поверхностное копирование, то есть копирование без включения вложенных объектов или массивов, то можно просто выполнить const simpleEvent = { title: "Builder.io Conf", } // ✅ нет вложенных объектов или массивов const shallowCopy = {...calendarEvent}
Или даже один из этих вариантов, если хотите: Но как только появляются вложенные элементы, мы сталкиваемся с проблемой: Как видно, мы не сделали полную копию этого объекта. Вложенные дата и массив по-прежнему являются общей ссылкой для оригинала и «копии». Это может привести к проблеме – если мы захотим отредактировать их, думая, что обновляем только скопированный объект события календаря. На самом деле это отличный хак и на удивление производительный, но с некоторыми недостатками, которые устраняет Возьмем для примера: Если вывести Мы хотели не этого. Это произошло потому, что Что-то Скажем, если мы скопируем пример То мы получим: Фу! И да, пришлось удалить циклическую ссылку, которая у нас изначально для этого была, поскольку Метод До сих пор распространенным решением этой проблемы была функция Она действительно работает так, как ожидается: Но с одной оговоркой. Согласно данным работы расширения Import Cost в IDE, которое выводит вес в Кб всего, что я импортирую, эта функция занимает 17,4 Кб в сжатом виде (5,3 Кб в архиве): Это предполагает, что вы импортируете только эту функцию. Если вместо этого импортировать более распространенным способом, не принимая в расчет, что 25 Кб только для этой одной функции. Хотя это и не станет концом света, в нашем случае это просто не нужно – не тогда, когда браузеры уже имеют встроенный Иначе они вызовут исключение Также выбрасывают исключение Также не клонируются аналогичные метадата-подобные фичи. К примеру, при использовании геттера клонируется результирующее значение, но не сама функция геттера (или любые другие метаданные свойства): Не происходит обход цепочки прототипов. Поэтому в случае клонирования экземпляра Все, что не входит в приведенный ниже список, клонировать нельзя: И здесь самое интересное – Правда, с одной оговоркой – поддержка Web Workers более ограничена: Источник: MDN Мы долго этого ждали, и теперь у нас наконец-то есть В заключение статьи приглашаем на открытое занятие «Прототипное наследование в JavaScript», которое состоится завтра вечером. На занятии мы разберемся, что такое прототипное наследование и как оно может помочь при разработке программ. В результате вы лучше поймете объектную модель Javascript и сможете писать ООП код с экономией памяти. Запись на урок открыта по ссылке.const shallowCopy = Object.assign({}, simpleEvent) const shallowCopy = Object.create(simpleEvent)const calendarEvent = { title: "Builder.io Conf", date: new Date(123), attendees: ["Steve"] } const shallowCopy = {...calendarEvent} // ? упс - мы добавили "Bob" и в копию и в воригинальное событие shallowCopy.attendees.push("Bob") // ? упс - мы обновили дату копии и исходного события shallowCopy.date.setTime(456)Почему не JSON.parse(JSON.stringify(x))?
structuredClone.const calendarEvent = { title: "Builder.io Conf", date: new Date(123), attendees: ["Steve"] } // ? JSON.stringify преобразовал дату в строку const problematicCopy = JSON.parse(JSON.stringify(calendarEvent))ProblematicCopy, мы получим:{ title: "Builder.io Conf", date: "1970-01-01T00:00:00.123Z" attendees: ["Steve"] }date должен быть не строкой, а объектом Date.JSON.stringify может обрабатывать только базовые объекты, массивы и примитивы. Любой другой тип может быть обработан непредсказуемым образом. Например, Dates преобразуются в string. Но Set просто преобразуется в {}.JSON.stringify даже игнорирует – например, undefined или функции.kitchenSink с помощью этого метода:const kitchenSink = { set: new Set([1, 3, 3]), map: new Map([[1, 2]]), regex: /foo/, deep: { array: [ new File(someBlobData, 'file.txt') ] }, error: new Error('Hello!') } const veryProblematicCopy = JSON.parse(JSON.stringify(kitchenSink)){ "set": {}, "map": {}, "regex": {}, "deep": { "array": [ {} ] }, "error": {}, }JSON.stringify просто выдает ошибки, если встречается с одной из них.JSON.stringify удобен, в случае если наши требования соответствуют его возможностям. Однако с помощью StructuredClone можно сделать многое из того, чего не может JSON.stringify.Почему не _.cloneDeep?
cloneDeep библиотеки Lodash.import cloneDeep from 'lodash/cloneDeep' const calendarEvent = { title: "Builder.io Conf", date: new Date(123), attendees: ["Steve"] } // ✅ Все в порядке const clonedEvent = structuredClone(calendarEvent)

structuredClone.Что structuredClone не может клонировать
Функции
DataCloneError:// ? Ошибка! structuredClone({ fn: () => { } })Узлы DOM
DataCloneError:// ? Ошибка! structuredClone({ el: document.body })Дескрипторы свойств, сеттеры и геттеры
structuredClone({ get foo() { return 'bar' } }) // Становится: { foo: 'bar' }Прототипы объектов
MyClass клонированный объект больше не будет известен как экземпляр этого класса. Но все валидные свойства этого класса будут клонированы.class MyClass { foo = 'bar' myMethod() { /* ... */ } } const myClass = new MyClass() const cloned = structuredClone(myClass) // Становится: { foo: 'bar' } cloned instanceof myClass // ложьПолный список поддерживаемых типов
JS Built-ins
Array, ArrayBuffer, Boolean, DataView, Date, Error types (указанные в списке ниже), Map , Object (но только простые объекты – например, из объектных литералов), примитивные типы (за исключением symbol – number, string, null, undefined, boolean, BigInt), RegExp, Set, TypedArrayError types (Ошибки типизации)
Error, EvalError, RangeError, ReferenceError , SyntaxError, TypeError, URIErrorWeb/API типы
AudioData, Blob, CryptoKey, DOMException, DOMMatrix, DOMMatrixReadOnly, DOMPoint, DomQuad, DomRect, File, FileList, FileSystemDirectoryHandle, FileSystemFileHandle, FileSystemHandle, ImageBitmap, ImageData, RTCCertificate, VideoFrameПоддержка браузеров и сред выполнения
structuredClone поддерживается во всех основных браузерах, и даже в Node.js и Deno.
Заключение
structuredClone, благодаря которому глубокое клонирование объектов в JavaScript становится простым делом. Спасибо, Surma.
ссылка на оригинал статьи https://habr.com/ru/articles/719460/
Добавить комментарий