Мы, люди, любят иметь дело в абсолютах. Это просто. Нюанс сложно. К сожалению для нас, все связано с нюансом. Вот почему мы должны задаться вопросом, если мы начнем задаваться вопросом, всегда ли плохая мутация.
Привет, если бы вам понравилась эта статья, я бы хотел, если бы вы Подписался на мою бесплатную еженедельную рассылку Анкет 1500+ других разработчиков уже зарегистрировались и выравнивают свои навыки веб -разработчика со мной!
Правда в том, что мутация не всегда плохая, и это не Обычно Плохо. Это просто это Анкет Это отличный инструмент, которые некоторые языки дают нам для манипулирования объектами. Как и в случае с любым инструментом, мы обязаны правильно использовать его.
Вот быстрое освежение для мутации объекта. Допустим, у нас есть объект человека:
const person = { name: 'Jarvis', age: 32 };
Если бы мы изменили возраст этого человека, мы будем мутировали объект:
person.age = 33;
Это кажется безобидным, верно?
Программирование – это все о Коммуникация и ожидания . Мутация идет не так, когда намерение операции не четко сообщается, и когда нарушаются ожидания разработчика (или машины).
Давайте рассмотрим следующее (плохое) использование мутации:
function copyPerson(person, newName, newAge) {
const newPerson = person;
newPerson.name = newName;
newPerson.age = newAge;
return newPerson;
}
Почему это плохо? Что ж, давайте посмотрим, что происходит, когда мы используем эту функцию в дикой природе:
const jarvis = { name: 'Jarvis', age: 32, arms: 2, legs: 2 };
const stanley = copyPerson(jarvis, 'Stanley', 27);
console.log(stanley);
// { age: 27, arms: 2, legs: 2, name: "Stanley" }
console.log(jarvis);
// { age: 27, arms: 2, legs: 2, name: "Stanley" }
Ожидания были полностью нарушены!
В нашем Copyperson Функция, мы случайно назначили Newperson Ссылка на то же самое человек объект. Поскольку они ссылаются на тот же объект, мутирующее Newperson Также мутирует человек Анкет
Как мы это исправить? Мы можем сделать это полностью без мутации, копировав человек Объект с использованием оператора спреда и одновременно перезаписывает имя и возраст характеристики:
function copyPerson(person, newName, newAge) {
const newPerson = {
...person,
name: newName,
age: newAge,
};
return newPerson;
}
И это сработает! Но мы также можем заставить его работать с Мутация, и это совершенно нормально. Некоторые могут даже найти его более читабельным!
function copyPerson(person, newName, newAge) {
const newPerson = { ...person };
newPerson.name = newName;
newPerson.age = newAge;
return newPerson;
}
Так что подождите, если это нормально, была ли мутация на самом деле виновником? Нет, это не было. Это было наше отсутствие понимания того, как работают ссылки Анкет
Популярные фронтальные рамки, такие как React, используют ссылки на логику рендеринга. Давайте рассмотрим следующий пример:
function App() {
const [person, setPerson] = useState({ name: 'Jarvis', age: 32 });
return ;
}
В этом примере Personcard Компонент будет повторно рендеринг, если человек изменения.
На самом деле, давайте будем более осторожны в нашей формулировке здесь: Personcard Компонент будет повторно рендеринг человек Ссылки на новый объект Анкет Опять же, мы можем попасть в беду, если мы мутируем человек а не создавать новый объект.
По этой причине следующий код будет багги:
function App() {
const [person, setPerson] = useState({ name: 'Jarvis', age: 32 });
function incrementAge() {
person.age++;
setPerson(person);
}
return (
<>
);
}
Если мы нажмите кнопку «Иметь день рождения», мы увеличиваем возраст Собственность человек объект, а затем попытайтесь установить человек Установите этот объект. Проблема в том, что это не Новый Объект, это то же самое человек Объект как предыдущий рендер! Алгоритм различия React не видит никаких изменений в человек Ссылка и не повторно заполняет Personcard Анкет
Как мы это исправить? Вы уже догадались: мы просто должны убедиться, что мы создаем новый объект на основе человек . Затем мы можем либо выполнить задачу, мутируя новый объект, либо какие -то другие средства:
function App() {
const [person, setPerson] = useState({ name: 'Jarvis', age: 32 });
function incrementAge() {
const newPerson = { ...person };
newPerson.age++;
setPerson(newPerson);
}
return (
<>
);
}
Если ваш инстинкт здесь, это мутирующее новый человек Это плохо, потому что мы используем React, обязательно проверьте свои предположения! Здесь нет ничего плохого: Newperson является переменной, оцениваемой для Прирастание функция Мы не мутируем что -то, что реагирует, отслеживает, и поэтому тот факт, что мы «в Реатике» не вступает в игру здесь.
Опять же, здесь очень важно признать, что мутация не плохая. Наше недопонимание ссылок на объекты и алгоритм раздачивания React – это то, что привело к поведению багги здесь.
Теперь, когда я обсуждал некоторые сценарии, в которых мутация часто обвиняют в поведении багги, давайте поговорим о том, когда мутация действительно сияет.
Ясность
Часто я считаю, что мутация является более ясной. Один пример, который я люблю использовать, – если нам нужно создать новый массив с одним из элементов в массиве. Работая в React, я часто видел следующее:
function updateItem(index, newValue) {
const newItems = items.map((el, i) => {
if (i === index) {
return newValue;
}
return el;
});
setItems(newItems);
}
И это работает нормально, но это немного сбивает с толку и, вероятно, немного сложно читать для тех, кто не свободно говорит в методах массива JavaScript.
Более читаемая альтернатива, на мой взгляд, состоит в том, чтобы просто создать копию первоначального массива, а затем мутировать соответствующий индекс скопированного массива:
function updateItem(index, newValue) {
const newItems = [...items];
newItems[index] = newValue;
setItems(newItems);
}
Я думаю, что это много более четкий.
Работа со сложными структурами
Одним из моих любимых примеров того, где сияет изменение, является создание структуры дерева. Вы можете сделать это в O (n) Time Все благодаря ссылкам и мутации.
Рассмотрим следующий массив, который представляет собой сплющенное дерево:
const data = [
{ id: 56, parentId: 62 },
{ id: 81, parentId: 80 },
{ id: 74, parentId: null },
{ id: 76, parentId: 80 },
{ id: 63, parentId: 62 },
{ id: 80, parentId: 86 },
{ id: 87, parentId: 86 },
{ id: 62, parentId: 74 },
{ id: 86, parentId: 74 },
];
Каждый узел имеет я бы а затем я бы своего родительского узла ( parentid ). Наш код для создания дерева может быть следующим:
// Get array location of each ID
const idMapping = data.reduce((acc, el, i) => {
acc[el.id] = i;
return acc;
}, {});
let root;
data.forEach((el) => {
// Handle the root element
if (el.parentId === null) {
root = el;
return;
}
// Use our mapping to locate the parent element in our data array
const parentEl = data[idMapping[el.parentId]];
// Add our current el to its parent's `children` array
parentEl.children = [...(parentEl.children || []), el];
});
Как это работает, мы сначала переходят через данные Массив один раз, чтобы создать отображение того, где каждый элемент находится в массиве. Затем мы делаем еще один проход через данные Массив и, для каждого элемента, мы используем отображение, чтобы найти его родителя в массиве. Наконец, мы мутат родитель Дети свойство, чтобы добавить текущий элемент в него.
Если мы Console.log (root) , в итоге мы получим полное дерево:
{
id: 74,
parentId: null,
children: [
{
id: 62,
parentId: 74,
children: [{ id: 56, parentId: 62 }, { id: 63, parentId: 62 }],
},
{
id: 86,
parentId: 74,
children: [
{
id: 80,
parentId: 86,
children: [{ id: 81, parentId: 80 }, { id: 76, parentId: 80 }],
},
{ id: 87, parentId: 86 },
],
},
],
};
Это действительно изящно и довольно сложно достичь без мутации.
Со временем я понял, что есть несколько ключевых моментов, чтобы понять в отношении мутации:
- Часто мы обвиняем мутацию в нашем собственном отсутствии понимания того, как работают ссылки.
- Популярные фронтальные рамки, такие как React, полагаются на сравнение ссылок на объекты для логики рендеринга. Мутирование более старых версий государства вызывает всевозможные головные боли и трудно понять ошибки. Вместо того, чтобы распознавать нюансы, разработчики часто избегают мутации полностью в любом месте в рамках кода React.
- Мутация – это превосходно Инструмент, когда его использование четко передано.
- Мутация – это превосходно Инструмент, если локализованный (например, мутированный объект никогда не избегает функции).
Оригинал: “https://dev.to/nas5w/mutation-isnt-always-bad-in-javascript-5e94”