В JavaScript вы можете использовать метод JSON.parse(JSON.stringify(obj)) для глубокого клонирования объекта. Этот метод преобразует объект в строку JSON, а затем анализирует строку обратно в новый объект, создавая новую ссылку в памяти.

let originalObject = {a: 1, b: {c: 2}};
let clonedObject = JSON.parse(JSON.stringify(originalObject));
console.log(clonedObject); // {a: 1, b: {c: 2}}

Однако этот метод не работает для функций Date и Symbol в объекте.

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

let originalObject = {a: 1, b: {c: 2}, d: function(){}};
let clonedObject = _.cloneDeep(originalObject);
console.log(clonedObject); // {a: 1, b: {c: 2}, d: function(){}}
function deepClone(obj) {
    let clone = Object.assign({}, obj);
    Object.keys(clone).forEach(
        key => (clone[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
    );
    return Array.isArray(obj) ? (clone.length = obj.length) && Array.from(clone) : clone;
}

Использование spread operator (...) для создания нового объекта со свойствами исходного объекта.

let originalObject = {a: 1, b: {c: 2}};
let clonedObject = {...originalObject};
console.log(clonedObject); // {a: 1, b: {c: 2}}

Используя метод Object.create(). Этот метод создает новый объект с исходным объектом в качестве прототипа.

let originalObject = {a: 1, b: {c: 2}};
let clonedObject = Object.create(originalObject);
console.log(clonedObject); // {}

Использование метода Object.assign() в сочетании с рекурсией

function deepClone(obj) {
    let clone = Object.assign({}, obj);
    Object.keys(clone).forEach(
        key => (clone[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
    );
    return Array.isArray(obj) ? (clone.length = obj.length) && Array.from(clone) : clone;
}

Использование методов JSON.parse() и JSON.stringify() с функцией замены, которая может обрабатывать циклические ссылки в объекте.

 function deepClone(obj) {
    let objects = new WeakMap();
    return JSON.parse(
        JSON.stringify(obj, (key, value) => {
            if (typeof value === 'object' && value !== null) {
                if (objects.has(value)) {
                    return;
                }
                objects.set(value, true);
            }
            return value;
        })
    );
}

С помощью рекурсивной функции

function deepClone(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }

    let clone = Array.isArray(obj) ? [] : {};

    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            clone[key] = deepClone(obj[key]);
        }
    }

    return clone;
}

В приведенном выше примере функция deepClone() принимает объект в качестве параметра и проверяет, является ли он null объектом или нет. Если это так, он возвращает исходный объект. Если это объект, он создает новый объект (либо массив, либо объект) и присваивает его переменной с именем clone. Затем он использует цикл for-in для перебора всех свойств исходного объекта и для каждого свойства рекурсивно вызывает функцию deepClone(), передавая значение свойства в качестве параметра. Таким образом, он проверит, является ли значение свойства объектом или нет, и рекурсивно клонирует его. Наконец, клонированный объект возвращается.

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

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

function deepClone(obj, cache = new WeakMap()) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }

    if (cache.has(obj)) {
        return cache.get(obj);
    }

    let clone = Array.isArray(obj) ? [] : {};
    cache.set(obj, clone);

    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            clone[key] = deepClone(obj[key], cache);
        }
    }

    return clone;
}

Вышеупомянутым способом вы можете избежать переполнения стека рекурсии при клонировании объектов с циклическими ссылками.

Сводка

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