Страницы

Поиск по вопросам

воскресенье, 21 октября 2018 г.

Основы JavaScript, копия объектов

Я заметил, что если есть какой-то объект, и если создать новый, присвоив ему значение этого объекта, и поменять в нем какое-то поле, то в старом оно тоже поменяется:
var a = {prop: 1}; document.getElementById("1").textContent = a.prop + ''; var b = a; b.prop = 2; document.getElementById("2").textContent = a.prop + '';


Какие есть способы создать копию объекта? То есть, чтобы изменения новой переменной не влияли на старую.


Ответ

Весь смысл в том, что в Javascript все объекты присваиваются по ссылке без исключений, это означает, что скольким бы переменным вы не присвоили объект, он всегда будет оставаться в памяти в единственном числе.
var a = {prop: 1} var b = a; var c = b; (c===b&&b===a&&c===a) // true
Пока хоть одна переменная ссылается на объект, он продолжает существовать. Когда последняя переменная, ссылающаяся на объект удаляется, удаляется и сам объект.
var a = {prop: 1}; var b = a; a.prop = 2; delete a; (b.prop===2); // true
В javascript нету оператора, позволяющего сделать копию объекта, поэтому копирование объектов в Javascript сводится к созданию нового с точно такими же свойствами, как у первого.
var a = {prop: 1}; var b = {}; b.prop = a.prop;
Однако нужно учитывать тот факт, что свойство объекта может содержать другой объект, который так же будет передан по ссылке, в случае присваивания, поэтому следующий пример создаст не правильный клон:
var a = { prop: { sub: 1 } } var b = {}; b.prop = a.prop;
Потому что свойство sub будет одним и тем же для обоих объектов.
a.prop.sub = 2; (b.prop.sub===1) // False
Поэтому при создании копии объекта необходимо проверять не являются ли его свойства объектами, и в этом случае производить с ними ту же операцию, что и мы провернули с a и b.
В целом функция создания клона объекта будет выглядеть так:
function makeClone(obj) { let clone = {}; // Создаем новый пустой объект for (let prop in obj) { // Перебираем все свойства копируемого объекта if (obj.hasOwnProperty(prop)) { // Только собственные свойства if ("object"===typeof obj[prop]) // Если свойство так же объект clone[prop] = makeClone(obj[prop]); // Делаем клон свойства else clone[prop] = obj[prop]; // Или же просто копируем значение } } return clone; }
Однако эта функция не универсальна и сработает правильно не во всех случаях, так как в Javascript помимо простых объектов, есть так же встроенные объекты и объекты созданные из прототипов, которые уже не получится просто так взять и скопировать. Это экземпляры Image, Date, HTMLElement, кастомные классы и пр. Кроме того бывают случаи когда вложенные свойства содержат ссылки на объекты, косвенно возвращающие нас к самим же себе. Например:
var a = {}, b = {prop: a}; a.prop = b;
Если вы запустите функцию makeClone для переменной а, то процесс создания клона будет происходить бесконечно, что вызовет "Maximum call stack size exceeded". Так что существует очень много нюансов клонирования объекта, поэтому лучше не изобретать велосипед, а воспользоваться готовыми решениями такими как jQuery.extend:
var a = {prop: 1}; var clone = jQuery.extend(true, {}, a);
Кроме того stand-alone аналоги этой функции можно найти на npmjs.
ЗЫ: По поводу Object.assign - это экспериментальный метод и пока он не поддерживается браузерами, и кажется babel без специальных плагинов его тоже не воспринимает.

Комментариев нет:

Отправить комментарий