Используйте стек.
Например, в стек помещаем комплекты из трёх значений - под каким именем, что и куда копируем. Крутим цикл, пока стек не окажется пуст. Если копируемое значение является примитивным, то просто записываем его в копию, и уходим на следующую итерацию. Если же перед нами объект, то закидываем в копию пустой объект; этим же свежесозданным пустым объектом дополняем пары ключ-значение исходного объекта, и кладём это всё в стек.
function clone(value) {
const clone = [];
for (const stack = [ [ 0, value, clone ] ]; stack.length;) {
const [ k, v, target ] = stack.pop();
const isObj = v instanceof Object;
target[k] = isObj ? v.constructor() : v;
if (isObj) {
stack.push(...Object.entries(v).map(n => [ ...n, target[k] ]).reverse());
}
}
return clone[0];
}
Или, если в копируемом объекте могут быть циклические ссылки или какой-то из вложенных объектов встречается несколько раз и так же должно быть и в копии, то надо запоминать встреченные объекты, чтобы не обрабатывать их повторно - воспользуемся Map'ом, где ключами будут оригинальные объекты, а значениями их копии.
Как выглядит получение копии объекта: проверяем, встречался ли указанный объект ранее, если нет - создаём новый объект, сохраняем его в Map, а также кладём его вместе с оригиналом в стек, для последующей обработки; достаём из Map'а копию.
Крутим цикл, пока стек не пуст, на каждой итерации достаём из стека объект и его копию, перебираем свойства оригинала, записываем в копию копии значений.
function clone(value) {
const stack = [];
const clones = new Map;
const getClone = v => v instanceof Object
? (clones.has(v) || stack.push([ v, clones.set(v, v.constructor()).get(v) ]),
clones.get(v))
: v;
for (getClone(value); stack.length;) {
const [ source, target ] = stack.pop();
for (const k in source) if (Object.hasOwn(source, k)) {
target[k] = getClone(source[k]);
}
}
return getClone(value);
}
как правильнее всего будет это сделать?
Перестать заниматься ерундой и начать использовать готовые решения -
structuredClone или
cloneDeep, например.