Неизменные и коллекции в JavaScript
Я пытаюсь понять, как использовать Immutables в JavaScript/TypeScript, не тратя на это целый день. Я не совсем готов к погружению в Immutable.js, потому что он, кажется, оставляет вас в сухом и безопасном состоянии.
Итак, давайте рассмотрим пример, где у меня есть Array, где все элементы имеют тип MyType. В моем классе у меня есть метод, который ищет массив и возвращает копию соответствующего элемента, поэтому мы не редактируем оригинал. Скажем теперь, что позже мне нужно посмотреть, есть ли объект в массиве, но у меня есть копия, а не оригинал.
Каков стандартный метод обработки этого? Любой метод, который я могу придумать, чтобы определить, есть ли у меня этот элемент, будет проходить некоторую форму циклического обхода коллекции и посещения каждого элемента, а затем выполнять неуклюжее совпадение на равенство, независимо от того, превращают ли они оба в строки или используют третий тусовочная библиотека.
Я хотел бы использовать Immutables, но я продолжаю сталкиваться с такими ситуациями, которые заставляют их выглядеть довольно непривлекательно. Что мне не хватает?
2 ответа
Я подозреваю, что мое решение не "... стандартный метод обработки этого". Тем не менее, я думаю, что это, по крайней мере, способ сделать то, что я думаю, что вы спрашиваете.
Вы пишете, что у вас есть метод, который "... возвращает копию соответствующего элемента, поэтому мы не редактируем оригинал". Не могли бы вы изменить этот метод, чтобы он вместо этого возвращалкак оригинал, так и копию?
В качестве примера приведенная ниже стратегия включает извлечение как исходного элемента из массива (который впоследствии может быть использован для поиска по ссылке), так и клона (которым можно манипулировать по мере необходимости, не затрагивая оригинал). По-прежнему стоит заплатить за клонирование оригинала во время поиска, но, по крайней мере, вам не нужно делать такие преобразования для каждого элемента в массиве, когда вы позже выполняете поиск в массиве. Более того, он даже позволяет вам различать элементы массива, которые идентичны по значению, что было бы невозможно, если бы вы только первоначально извлекли копию элемента. Приведенный ниже код демонстрирует это, делая каждый элемент массива идентичным по значению (но, по определению объектов, по ссылке).
Я не знаю, нарушает ли это другие лучшие практики неизменяемости, например, сохраняя копии ссылок на элементы (что, я полагаю, оставляет код открытым для будущих нарушений неизменяемости, даже если они в настоящее время не нарушаются... хотя вы может глубоко заморозить оригинал, чтобы предотвратить будущие мутации). Однако, по крайней мере, это позволяет вам сохранять все технически неизменным, в то же время сохраняя возможность поиска по ссылке. Таким образом, вы можете изменять свой клон столько раз, сколько хотите, но при этом всегда держитесь за связанную копию оригинала по ссылке.
const retrieveDerivative = (array, elmtNum) => {
const orig = array[elmtNum];
const clone = JSON.parse(JSON.stringify(orig));
return {orig, clone};
};
const getIndexOfElmt = (array, derivativeOfElement) => {
return array.indexOf(derivativeOfElement.orig);
};
const obj1 = {a: {b: 1}}; // Object #s are irrelevant.
const obj3 = {a: {b: 1}}; // Note that all objects are identical
const obj5 = {a: {b: 1}}; // by value and thus can only be
const obj8 = {a: {b: 1}}; // differentiated by reference.
const myArr = [obj3, obj5, obj1, obj8];
const derivedFromSomeElmt = retrieveDerivative(myArr, 2);
const indexOfSomeElmt = getIndexOfElmt(myArr, derivedFromSomeElmt);
console.log(indexOfSomeElmt);
Вы описали ситуацию, в которой изменяемая структура данных имеет очевидные преимущества, но если вы иначе извлекаете выгоду из использования неизменяемых, есть более подходящие подходы.
Сохранение его неизменным означает, что ваш новый обновленный объект является совершенно новым, что сокращает оба пути: у вас может быть новый объект, но у вас также есть доступ к исходному объекту! С этим вы можете сделать много полезных вещей, например, связать свои объекты, чтобы у вас была история отмены, и вы можете вернуться назад во времени, чтобы откатить изменения.
Так что не используйте некоторые хакерские свойства поиска в массиве. Проблема с вашим примером заключается в том, что вы создаете новый объект в неподходящее время: у вас нет функции, возвращающей копию объекта. Пусть функция вернет исходный объект и вызовет ваше обновление, используя исходный объект в качестве индекса.
let myThings = [new MyType(), new MyType(), new MyType()];
// We update by taking the thing, and replacing with a new one.
// I'll keep the array immutable too
function replaceThing(oldThing, newThing) {
const oldIndex = myThings.indexOf(oldThing);
myThings = myThings.slice();
myThings[oldIndex] = newThing;
return myThings;
}
// then when I want to update it
// Keep immutable by spreading
const redThing = myThings.find(({ red }) => red);
if (redThing) {
// In this example, there is a 'clone' method
replaceThing(redThing, Object.assign(redThing.clone(), {
newProperty: 'a new value in my immutable!',
});
}
Все это говорит о том, что занятия делают это намного сложнее. Гораздо проще сохранять неизменность простых объектов, поскольку вы можете просто распространить старый объект на новый, например: { ...redThing, newProperty: 'a new value' }
, Как только вы получите объект выше 1 высоты, вы можете найти immutable.js гораздо более полезным, так как вы можете mergeDeep
,