Создание иммутабельной копии объекта
Здравствуйте. Есть ли какие-нибудь библиотеки, которые делают иммутабельные копии объектов, но без указания пути вложенности подобъектов? Например есть переменная container хранящая объект с большой глубиной вложенных подобъектов. И есть другая переменная obj хранящая ссылку на объект вложенный в container (неизвестно на каком уровне вложенность). И нужно создать иммутабельную копию container чтобы изменить объект, на который указывает obj. При этом оригинальный объект был бы не затронут. То есть функция, которая должна это сделать (на рисунке называется makeImmutableCopy), должна найти в container расположение объекта, на который указывает obj, и сделать поверхностные копии каждого родительского объекта (на рисунке они имеют зелёную обводку).
В документации Реакта указываются библиотеки https://github.com/immerjs/immer и https://github.com/kolodny/immutability-helper. Но они требуют указывать полный путь, поэтому не подходят. |
Написал функцию делающую неизменяемую копию: https://github.com/AndrewKozinsky/ma...mutableCopy.js
|
Цитата:
Цитата:
Когда нет совпадения, что скопировать, то должен возвращаться исходный объект без изменении (не так ли?), а у вас выкидывает исключение! Интересный способ проверки объекта путём приведения к примитиву! А если такое приведение не возможно, то выкидывает исключение! Фактически вы ничего не меняете в объекте, зачем нам нужен новый объект, в котором ничего не изменено. Это очень не очевидное API, которое способствует ошибкам. Используйте immer, в котором происходит копирование только при реальных изменениях! В отличие от вашего небезопасного кода, в immer исходный объект не изменяется и при одинаковых аргументах, выдаётся одинаковый результат. Вот пример... <script src="https://unpkg.com/immer/dist/immer.umd.js"></script> <script> //Например есть массив arr_1. Требуется сделать его копию и //изменить подмассив по адресу arr_1[2][2][1]. const arr_1 = [ 0, [1, 2, 3], [ 4, [5, 6], [ [7, 8], [ 9, 10 ] ], ] ]; //Поэтому в функцию immer.produce передаю ссылки на //оригинальный массив и функцию, в которой изменяется //подмассив. Функция возвращает копию реальных изменении. const copy = immer.produce(arr_1, arr_1 => { const link = arr_1[2][2][1]; // реально меняем подмассив link.push(40); }); console.log(copy); //Можно сделать проверки console.log(arr_1 === copy); // false console.log(arr_1[1] === copy[1]); // true — массивы равны потому что он не будет затронут, поэтому нет смысла его копировать. console.log(arr_1[2] === copy[2]); // false — массив был скопирован потому что является предком изменяемого массива console.log(arr_1[2][2] === copy[2][2]); // false console.log(arr_1[2][2][0] === copy[2][2][0]); // true </script> Смотри в консоли |
Цитата:
Цитата:
А immer мне не подходит потому что в моём случае не известен полный путь до объекта, который будет изменён. |
Обновил функцию. Теперь она получает объект, который нужно скопировать, затем объект, который нужно найти и объект, на который нужно заменить объект из второго параметра. Возвращает неизменяемую копию.
|
Цитата:
Вот настоящий неизменяемый объект... var a = Object.freeze({ id: Object.freeze({ value: 60 }), string: Object.freeze({ value: Object.freeze(["abc", 123]) }) }); // попробуем изменить a.id.value = 77; // не изменилось, а у вас изменяется alert(a.id.value); После вашей функции даже копия настоящего неизменяемого объекта становится изменяемой! Цитата:
var a = { __proto__: null, id: { value: 60 } }; После вашей функции происходит неожиданная смена конструктора! Объект без конструктора вы можете отсеять так... mainData instanceof Object Цитата:
mainData instanceof Array А если это типизированный массив? |
Цитата:
Вы правы насчет того, что если в копируемом объекте установлен другой прототип, то он будет заменён на Object. Как я понимаю проще всего всего в этом случае копировать через деструктуризацию: {...newData} Но мне это пока не требуется. Существующий код мне подходит. Так же не использую в работе типизированные массивы. |
Часовой пояс GMT +3, время: 19:22. |