Как часто вы пытались клонировать элемент в Javascript, но результат был не таким, как вы ожидали? В этой статье мы объясним основные идеи, лежащие в основе клонирования, чтобы гарантировать, что вы обычно используете лучший выбор.
Разница между поверхностным копированием и глубоким копированием
Javascript имеет 2 типа копирования объекта:
1. Неглубокое копирование: подразумевает, что реплицируется первый уровень элемента. Ссылаются на более глубокие уровни.
2. Глубокое копирование: означает, что копируются все уровни объекта. Это точная копия объекта.
Для получения более глубокого опыта я попросил ChatGPT сгенерировать мне вложенный объект со всеми возможными типами данных, и он вернул мне это:
const nestedObject1 = { string: 'Hello, world!', number: 42, boolean: true, nullValue: null, undefinedValue: undefined, array: ['apple', 'banana', 'cherry'], object: { nestedString: 'This is a nested string', nestedNumber: 123, nestedBoolean: false }, date: new Date(), regexp: /hello/g, function: function () { console.log('This is a function'); }, symbol: Symbol('symbol') };
Назначение объекта:
Самый простой и худший метод копирования — использование оператора равенства (=).
let object2 = nestedObject1 object2.string = "Hello from object2" object2.number = 45 object2.array[2] = "Strawberry" object2.object.nestedNumber = 66
Этот метод не создает новый объект. Вместо этого он просто создает новую ссылку на сам старый объект. Любое обновление объекта 2 сильно влияет на объект 1 и наоборот.
Поверхностное копирование:
Чтобы получить поверхностное копирование, мы можем использовать оператор расширения ({…}) или функцию Object.assign().
- Оператор спреда
let object2 = { ...nestedObject1 } object2.string = "Hello from object2" object2.number = 45 object2.array[2] = "Strawberry" object2.object.nestedNumber = 66
Если мы посмотрим на результат, мы увидим интересную вещь:
Изменения уровня 1 в объекте 2 не изменились в полях объекта 2. Однако более глубокие изменения уровня затронули оба объекта и изменили оба значения.
Это связано с тем, что неглубокая копия только что создала еще один указатель на более глубокие уровни и, следовательно, создала ссылку, а не копию.
2. Функция Object.assign()
let object2 = Object.assign({}, nestedObject1) object2.string = "Hello from object2" object2.number = 45 object2.array[2] = "Strawberry" object2.object.nestedNumber = 66
и результат
Мы видим аналогичный результат для оператора распространения.
Глубокое копирование
Под глубокой копией понимается создание новой копии объекта или массива, полностью независимой от оригинала, поэтому любые изменения, внесенные в копию, не повлияют на оригинал, и наоборот. Глубокая копия гарантирует, что все свойства и вложенные объекты или массивы также копируются, а не просто копируются ссылки на исходный объект или массив.
Чтобы получить глубокую копию, мы рассмотрим следующие методы:
— взломать JSON.parse и JSON.stringify
— использовать пользовательскую функцию
— использовать сторонний библиотека (lodash)
— встроенный API
- Взлом JSON.parse и JSON.stringify
Чтобы использовать это, мы используем JSON.parse последовательно с JSON.stringify следующим образом:
let object2 = JSON.parse(JSON.stringify(nestedObject1)) object2.string = "Hello from object2" object2.number = 45 object2.array[2] = "Strawberry" object2.object.nestedNumber = 66
На что мы получаем этот вывод
Как мы видим, новый объект претерпел массовую потерю данных. Наряду с этим изменился тип методов , таких как date.
Следовательно, этот метод крайне неэффективен при работе с непримитивными типами данных, такими как функции, регулярные выражения, символы, неопределенные и т. д.
2.Использование пользовательской функции
Я создал функцию глубокого копирования с именем deepcopy(), написал кучу кода и внес те же изменения.
function deepCopy(obj) { if (typeof obj !== 'object' || obj === null) { return obj; } let copy = Array.isArray(obj) ? [] : {}; Object.keys(obj).forEach((key) => { copy[key] = deepCopy(obj[key]); }); return copy; } let object2 = deepCopy(nestedObject1) object2.string = "Hello from object2" object2.number = 45 object2.array[2] = "Strawberry" object2.object.nestedNumber = 66
Мы получаем результат
Как мы видим, этот метод также вызвал массовую потерю данных, аналогичную методам JSON.parse и JSON.stringify.
3. Использование сторонней библиотеки (lodash)
На данный момент эта библиотека вернула очень хорошую копию данных с самой минимальной потерей данных и максимальной эффективностью.
Вы можете проверить это на https://lodash.com/
Чтобы использовать эту библиотеку, вам понадобится npm или yarn и скрипт типа модуля.
const load = require('lodash'); const object2 = load.cloneDeep(nestedObject1); object2.string = "Hello from object2" object2.number = 45 object2.array[2] = "Strawberry" object2.object.nestedNumber = 66
Результат выглядит следующим образом
Это был наиболее эффективный метод создания копии. Однако здесь остается проблема, заключающаяся в том, что не всегда нам нужен пакет или модуль в формате javascript. Для этого существует следующий метод.
4. Использование встроенного API
Используя structuredClone API, мы можем добиться глубокого копирования объектов с минимальной потерей данных. Однако этот метод не может выполнять копирование функций, символов. Итак, чтобы изменить объект, у нас теперь есть
const nestedObject1 = { string: 'Hello, world!', number: 42, boolean: true, nullValue: null, undefinedValue: undefined, array: ['apple', 'banana', 'cherry'], object: { nestedString: 'This is a nested string', nestedNumber: 123, nestedBoolean: false }, date: new Date(), regexp: /hello/g, } let object2 = structuredClone(nestedObject1) object2.string = "Hello from object2" object2.number = 45 object2.array[2] = "Strawberry" object2.object.nestedNumber = 66
При этом получаем следующий результат
API структурированного клона работает в браузерах, и, учитывая вышеупомянутые ограничения, он по-прежнему имеет оптимальное решение с учетом всевозможных ограничений.
Надеюсь, вы все нашли этот блог информативным и теперь подробно изучили различные методы копирования объектов.