Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 18.10.2019, 18:12
Аспирант
Отправить личное сообщение для Andrew K Посмотреть профиль Найти все сообщения от Andrew K
 
Регистрация: 15.11.2014
Сообщений: 50

Создание иммутабельной копии объекта
Здравствуйте. Есть ли какие-нибудь библиотеки, которые делают иммутабельные копии объектов, но без указания пути вложенности подобъектов? Например есть переменная container хранящая объект с большой глубиной вложенных подобъектов. И есть другая переменная obj хранящая ссылку на объект вложенный в container (неизвестно на каком уровне вложенность). И нужно создать иммутабельную копию container чтобы изменить объект, на который указывает obj. При этом оригинальный объект был бы не затронут. То есть функция, которая должна это сделать (на рисунке называется makeImmutableCopy), должна найти в container расположение объекта, на который указывает obj, и сделать поверхностные копии каждого родительского объекта (на рисунке они имеют зелёную обводку).

В документации Реакта указываются библиотеки https://github.com/immerjs/immer и https://github.com/kolodny/immutability-helper. Но они требуют указывать полный путь, поэтому не подходят.
Ответить с цитированием
  #2 (permalink)  
Старый 22.10.2019, 18:44
Аспирант
Отправить личное сообщение для Andrew K Посмотреть профиль Найти все сообщения от Andrew K
 
Регистрация: 15.11.2014
Сообщений: 50

Написал функцию делающую неизменяемую копию: https://github.com/AndrewKozinsky/ma...mutableCopy.js
Ответить с цитированием
  #3 (permalink)  
Старый 23.10.2019, 13:29
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

Сообщение от Andrew K
Написал функцию делающую неизменяемую копию
Цитата:
// Перебрать все элементы массива
        for(let i = 0; i < newData.length; i++) {

// Если это объект
    if(mainData + '' === "[object Object]") {
А кто будет отлавливать исключения?

Когда нет совпадения, что скопировать, то должен возвращаться исходный объект без изменении (не так ли?), а у вас выкидывает исключение!

Интересный способ проверки объекта путём приведения к примитиву! А если такое приведение не возможно, то выкидывает исключение!

Фактически вы ничего не меняете в объекте, зачем нам нужен новый объект, в котором ничего не изменено. Это очень не очевидное 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> Смотри в консоли

Последний раз редактировалось Malleys, 23.10.2019 в 13:35.
Ответить с цитированием
  #4 (permalink)  
Старый 31.10.2019, 18:45
Аспирант
Отправить личное сообщение для Andrew K Посмотреть профиль Найти все сообщения от Andrew K
 
Регистрация: 15.11.2014
Сообщений: 50

Сообщение от Malleys Посмотреть сообщение
А кто будет отлавливать исключения?
Когда нет совпадения, что скопировать, то должен возвращаться исходный объект без изменении
Спасибо за комментарий. Немного переписал функцию. Теперь если целевого объекта нет в объекте данных, то возвращает оригинальный объект данных.


Сообщение от Malleys Посмотреть сообщение
Фактически вы ничего не меняете в объекте, зачем нам нужен новый объект, в котором ничего не изменено. Это очень не очевидное API, которое способствует ошибкам. Используйте immer, в котором происходит копирование только при реальных изменениях! В отличие от вашего небезопасного кода, в immer исходный объект не изменяется и при одинаковых аргументах, выдаётся одинаковый результат.
По логике моей функции сначала создаётся копия объекта данных, а затем меняется. Но сейчас мне кажется вы правы и следует сделать еще один аргумент и сразу передавать изменённый объект данных.

А immer мне не подходит потому что в моём случае не известен полный путь до объекта, который будет изменён.
Ответить с цитированием
  #5 (permalink)  
Старый 07.11.2019, 17:02
Аспирант
Отправить личное сообщение для Andrew K Посмотреть профиль Найти все сообщения от Andrew K
 
Регистрация: 15.11.2014
Сообщений: 50

Обновил функцию. Теперь она получает объект, который нужно скопировать, затем объект, который нужно найти и объект, на который нужно заменить объект из второго параметра. Возвращает неизменяемую копию.
Ответить с цитированием
  #6 (permalink)  
Старый 08.11.2019, 02:37
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

Сообщение от Andrew K
Возвращает неизменяемую копию.
Всё изменяется!
Вот настоящий неизменяемый объект...
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);


После вашей функции даже копия настоящего неизменяемого объекта становится изменяемой!

Сообщение от Andrew K
// Скопировать объект и вставить как значение возвращаемого объекта
newData = Object.assign({}, mainData);
Может стоит учитывать исходный конструктор при создании объекта? А то был неизменяемый объект без конструктора, а стал изменяемым и с конструктором...
var a = {
	__proto__: null,
	id: { value: 60 }
};


После вашей функции происходит неожиданная смена конструктора!

Объект без конструктора вы можете отсеять так...
mainData instanceof Object


Сообщение от Andrew K
// Если это массив...
if(toString.call(mainData) === "[object Array]") {
А если это структура, которая была унаследована от класса Array. Почему её нельзя использовать? Может стоит определять так...
mainData instanceof Array


А если это типизированный массив?
Ответить с цитированием
  #7 (permalink)  
Старый 16.11.2019, 14:27
Аспирант
Отправить личное сообщение для Andrew K Посмотреть профиль Найти все сообщения от Andrew K
 
Регистрация: 15.11.2014
Сообщений: 50

Сообщение от Malleys
Всё изменяется!
Я использую свою функцию в сценариях и она отрабатывает так, как нужно. Может вы пытались сделать копию объекта с вложенными объектами? В этом случае вложенные объекты будут изменяемыми потому что функция не делает глубокую копию а только заменяет один объект на другой и делает копии элементов вверх по иерархии.

Вы правы насчет того, что если в копируемом объекте установлен другой прототип, то он будет заменён на Object. Как я понимаю проще всего всего в этом случае копировать через деструктуризацию:
{...newData}

Но мне это пока не требуется. Существующий код мне подходит. Так же не использую в работе типизированные массивы.
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Создание копии объекта даты AlexanderFilatov Общие вопросы Javascript 12 27.09.2018 12:19
canvas, svg или что то еще, создание сайта планировки объекта mmsgold Элементы интерфейса 2 29.11.2017 14:47
DOM создание объекта JQ ~ вопрос реализации состояния Brook Events/DOM/Window 0 21.01.2014 22:45
Странное создание объекта IceBird Общие вопросы Javascript 3 12.06.2010 05:39
Создание копий объекта - обязателен ли prototype? heh131 Общие вопросы Javascript 12 15.05.2010 12:55