Comments 37
let originalObject = {a: 1, b: 2, c: {d: 3, e: 4}};
let clonedObject = JSON.parse(JSON.stringify(originalObject));
clonedObject.c.d = 7;
console.dir(originalObject); // {"a":1,"b":2,"c":{"d":3,"e":4}}
console.dir(clonedObject); // {"a":1,"b":2,"c":{"d":7,"e":4}}
И зачем огород городить? :)
Другая тема — копирование методов…
Поиск в Гугле.
P.S. Если искать по сути, а не по названию функции, то результатов может оказаться немного больше.
const obj = { ... }
const clObj = JSON.parse(JSON.stringify(obj))
JSON.parse
, огромное спасибо ему за это. Как оказалась JSON.parse
уступает в производительности в 10 раз. Ссылка натестJSON не может содержать функции. А значит, склонировать без потерь получится не любой объект...
не встречал ничего лучшеНавскидку:
JSON.parse(JSON.stringify(undefined));
let d = new Date();
JSON.parse(JSON.stringify(d));
let child = {}, parent = {};
child.parent = parent; parent.child = child;
JSON.parse(JSON.stringify(child));
Для простых случаев вполне подойдет. Однако мы модем клонировать и пользщовательские объекты модем клонировать их вместе с методами, восстанавливать конструктор и соответственно прототип объекта
Как бы сказать помягче?
На выходе из вашей функции получается совсем не то, что должно быть.
Z object after cloning: { a: [ 1, 2, 3 ] }
Y object: { a: { '0': 1, '1': 2, '2': 3 }, addnlProp: { fd: 45 } }
function deepClone(obj) {
const test1 = { a: { d: [1, 2, 3]}, b: 5 };
const test2 = deepClone(test1);
test2.a.d.push('arr')
test2.b = 9;
console.log('test1 object: ', test1);
console.log('test2 object: ', test2);
function deepClone(obj) {
const clObj = {};
for(const i in obj) {
if (obj[i] instanceof Object && !(obj[i] instanceof Array)) {
clObj[i] = deepClone(obj[i]);
continue;
}
clObj[i] = obj[i];
}
return clObj;
}
Добавив одно условие, я скопировал массив верно, но при этом он остался зависимым от массива из другого объекта. Хотя подобное следовало ожидать, но все же интересная особенность
Все таки добью эту функцию, чтобы работала корректно не только с объектами, а как минимум еще с массивом. После чего внесу правки в статью. Еще раз, спасибо вам за отзыва )
По хорошему, Вам нужен вызов `obj[i].map(deepClone)`, в массиве могут быть не только скаляры.
Отличная идея с map, нужно попробовать ваш вариант на практике и посмотреть на результат, спасибо )
Ещё один совет: когда наиграетесь с этим велосипедом, поищите удовлетворяющую Вас open source альтернативу и используйте её — ни к чему тратить силы на самостоятельную поддержку подобных функций.
На примере deepClone: рано или поздно Вы столкнётесь с циклическими ссылками и начнёте решать эту проблему. Есть несколько способов её решения, Вам придётся выбирать. Потом будут новые проблемы, возможно захочется сохранить прототипы… Путь даже такой элементарной штучки довольно долог. Но в существующих реализациях зачастую весь этот путь уже пройден. И для решения вновь возникающих проблем у Вас есть огромный штат добровольцев(включая Вас).
В общем, если проблема не академическая или «на поиграться», всегда стоит начинать с поиска существующих решений. Жизнь одна, если каждый будет распыляться на подобные мелочи. ни у кого не хватит времени создать что-то стоящее.
Хм, растёкся я тут мыслью по древу. извините.
Думаю, в комментариях есть место ещё и для такой ссылочки: Deep-copying in JavaScript
Я бы еще рассмотрел кейс клонирования объектов с сохранением их прототипов: объекты могут содержать методы, которые мы хотим иметь и у клонов.
Каждый js-программист наверняка без труда ответит на этот вопрос, но все же скажем: примитивные типы данных передаются в функцию всегда только по значению, а ссылочные всегда только по ссылке.
Я не отношусь к категории "каждый программист" потому что думаю что в JS все параметры передаются в функцию по значению. Просто для объектов в качестве значения выступает ссылка на объект. Вы внутри функции можете поменять поля в этом объекте. Но не сможете присвоить параметру новое значение.
Второй момент про алгоритм клонирования. Вцелом направление правильное, на как минимум нужно учес еще и клонирование Array. Хотя если по-хорошему то нужно еще как-то работать с прототипами а также и с другими типами JS
То есть если у Вас объект Dog то его кот также должен быть объектом типа Dog
Способ один — значение. В качестве значения используется ссылка. Тем кто не программировал на более древних языках (Пскаль, Си, PL/1) это разница не совсем ясна
Еси переменная передается по ссылке то ее значение меняется если его изменить внутри функции. В JS Вы можете изменить сам объект и это будет видно извне.
Но если вы сделаете так
var obj0 = {}
function(obj1) {
obj1 = {}
return obj1
}
obj0 !== obj1 // true
Если бы объект передалася по ссылке то
obj0 === obj1 // true
Просто по ссылке они передаются неявным образом.
Передача переменной по ссылке на примере php: 3v4l.org/TQXg1
В javascript ничего подобного нет, если вы что-то присваиваете переменной, старое значение теряется.
Просто для объектов передаётся значение указателя на тот же объект, что позволяет мутировать этот объект, но не позволяет изменить саму переменную.
_.deepClone() ?
let num = 10;
let obj = { a: 5, b: 6 };
Как видим, в первом случае мы перезаписали значение переменной, а во втором расширили объект.Точно расширили?
function insertValToArr(arr, val) {
const newArr = [];
arr.forEach((value, ind) => { newArr[ind] = value});
newArr.push(val);
return newArr;
}
forEach здесь не нужен, лучше использовать map:
function insertValToArr(arr, val) {
const newArr = arr.map((value) => value);
newArr.push(val);
return newArr;
}
А ещё лучше сократить до однострочника:
const insertValToArr = (arr, val) => arr.map((value) => value).concat(val);
И в JS есть 7 примитивных типов данных и всего один ссылочный — Object (MDN). Array, Function, Maps, Sets и т. д. — это всё реализации объекта.
insertValToArr
. От себя могу добавить, что можно подобное сделать и через spread, но данный вариант скорее подойдет для одномерных массивов.function insertValToArr(arr, val) {
return [...arr, val];
}
Мы всегда можем создать объект без прототипа через Object.create. Так как у такого объекта не будет прототипа Object, то и логично, что instanceof не найдет его там.
const a = Object.create(null);
console.log(a instanceof Object) // false
const b = {};
console.log(b instanceof Object) // true
Независимое глубокое клонирование объектов в JavaScript