Как часто вы пытались клонировать элемент в 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().

  1. Оператор спреда
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

  1. Взлом 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 структурированного клона работает в браузерах, и, учитывая вышеупомянутые ограничения, он по-прежнему имеет оптимальное решение с учетом всевозможных ограничений.

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