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