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

Вот пример неглубокой копии в JavaScript:

const original = { a: 1, b: { c: 2 } };
const copy = original;

console.log(copy);  // { a: 1, b: { c: 2 } }

copy.a = 2;
console.log(original.a);  // 2 (the original object is modified)

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

function deepCopy(obj) {
  return JSON.parse(JSON.stringify(obj));
}

const original = { a: 1, b: { c: 2 } };
const copy = deepCopy(original);

console.log(copy);  // { a: 1, b: { c: 2 } }

Метод JSON.stringify() преобразует объект в строку JSON, а метод JSON.parse() анализирует строку JSON и создает из нее новый объект. Этот метод — быстрый и простой способ создать глубокую копию объекта, но он имеет некоторые ограничения. Он работает только с простыми объектами и массивами и не работает с объектами, имеющими функции или циклические ссылки.

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

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

  let copy;
  if (Array.isArray(obj)) {
    copy = [];
  } else {
    copy = {};
  }

  for (const key in obj) {
    copy[key] = deepCopy(obj[key]);
  }

  return copy;
}

const original = { a: 1, b: { c: 2 } };
const originalArray = [1,2,3,4,5,6,7];
const copyArray = deepCopy(originalArray);
const copy = deepCopy(original);

console.log(copy);  // { a: 1, b: { c: 2 } }
console.log(copyArray); // [1,2,3,4,5,6,7]

Эта функция использует рекурсивный подход для создания нового объекта с той же структурой, что и исходный объект. Он проверяет тип входного объекта и, если это массив или объект, создает новый массив или объект и рекурсивно копирует свойства входного объекта в новый объект. Если входной объект является примитивным значением (например, числом или строкой), он просто возвращает значение. Этот подход работает для более сложных объектов и правильно обрабатывает циклические ссылки.

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

  1. Объект, у которого есть свойство, ссылающееся на сам объект:
const obj = {};
obj.self = obj;

2. Объект, у которого есть свойство, ссылающееся на другой объект, содержащий ссылку на исходный объект:

const obj1 = {};
const obj2 = { parent: obj1 };
obj1.child = obj2;

3. Объект, у которого есть свойство, ссылающееся на массив, содержащий ссылку на объект:

const obj = {};
obj.array = [obj];

Циклические ссылки могут вызвать проблемы при попытке сериализовать объект с помощью JSON.stringify(), потому что процесс сериализации застрянет в бесконечном цикле, пытаясь сериализовать циклические ссылки. Чтобы избежать этой проблемы, вы можете использовать пользовательскую функцию замены с JSON.stringify() для фильтрации циклических ссылок при сериализации объекта.

function replacer(key, value) {
  if (value === this) {
    return undefined;
  }
  return value;
}

const obj = {};
obj.self = obj;

console.log(JSON.stringify(obj, replacer));  // {}