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