Неизменяемость — очень горячая тема в современном JavaScript, и причина этого, конечно же, в парадигме функционального программирования.

Неизменяемые данные тесно связаны с функциональным подходом, где любая мутация рассматривается как нежелательный побочный эффект. Но сначала давайте углубимся в детали изменчивости и неизменности.

Что такое изменчивость?

Просто чтобы все было ясно, нет ничего плохого в том, что код может быть изменен — API-интерфейс массива JavaScript является изменяемым, и в этом нет ничего плохого. Тем не менее, неправильное использование изменчивости может вызвать побочные эффекты для вашего программного обеспечения. Давайте посмотрим на пример кода ниже:

const person = {
   name: 'Rodrigo',
   email: '[email protected]'
} 
// Make a copy of person object 
const newPerson = person; 
// Changing the email of the new person 
newPerson.email = '[email protected]'; 
console.log(newPerson === person); // true 
console.log(person);    //  { name: 'Rodrigo', email: '[email protected]' } 
console.log(newPerson); //  { name: 'Rodrigo', email: '[email protected]' }

Вы можете видеть, что мы копируем объект (person) в другой объект (newPerson) и вносим небольшое изменение в newPerson. Проблема здесь в том, что изменение отражается на обоих объектах.

Это происходит потому, что когда вы назначаете объект переменной в JS, вы фактически назначаете ему ссылку на память, поэтому, когда вы делаете это:

const newPerson = person;

Вы просто копируете эту ссылку, а не реальные значения. Обе переменные указывают на одно и то же место.

Стать неизменным

Неизменяемость — это искусство поддерживать состояние объекта, делая разработку простой, отслеживаемой, тестируемой и уменьшая любые возможные побочные эффекты. Основная идея такова: обновление должно не изменять объект, а создавать новый объект с обновленными данными.

Вместо того, чтобы передавать объект и изменять его, нам будет лучше создать совершенно новый объект:

const person = { name: 'Rodrigo', email: '[email protected]' } 
const newPerson = Object.assign(
  {}, 
  person, 
  { email: '[email protected]' }
);
console.log(newPerson === person); // false 
console.log(person)    // { name: 'Rodrigo', email: '[email protected]' } 
console.log(newPerson) // { name: 'Rodrigo', email: '[email protected]' }

Но эй, мы используем ES6, нельзя ли сделать это по-другому? Конечно! Мы можем использовать оператор Spread! Взглянем:

const person = { name: 'Rodrigo', email: '[email protected]' } 
const newPerson = { ...person, email: '[email protected]' } 
console.log(newPerson === person); // false - really different objects
console.log(person)    // { name: 'Rodrigo', email: '[email protected]' } 
console.log(newPerson) // { name: 'Rodrigo', email: '[email protected]' }

Аккуратно, верно? Опять же, тот же результат и даже более чистый код. Сначала мы создаем новый объект, присваивая {} переменной, затем мы используем оператор «расширения» (…), чтобы скопировать все свойства человека в новый объект. Затем мы определяем новое свойство «электронная почта», которое переопределяет старое. Обратите внимание, что в этом случае порядок имеет значение, если электронная почта: ‘[email protected]’ определена до распространения объекта человека, она будет переопределена значением адреса электронной почты из объекта человека.

Массивы

Во-первых, давайте сделаем небольшой пример того, как вы можете изменить массив:

const fruits = [ 'Orange', 'Apple' ];
const newFruits = characters newFruits.push('Banana');
console.log(fruits === newFruits) // true :-(

Массивы работают так же, как объекты, поэтому вы также можете использовать оператор распространения, чтобы сделать массивы неизменяемыми. Вот как вы можете это использовать:

const fruits = [ 'Orange', 'Apple' ];
const newFruits = [ ...fruits, 'Banana' ];
console.log(fruits === newFruits) // false 
console.log(fruits)    // [ 'Orange', 'Apple' ] 
console.log(newFruits) // [ 'Orange', 'Apple', 'Banana' ]

Как видите, вы можете довольно легко добиться неизменности, используя простой современный JavaScript! В конце концов, все дело в здравом смысле и понимании того, что на самом деле делает ваш код. Если вы программируете невнимательно, JavaScript может быть непредсказуемым.

Дополнительные материалы на plainenglish.io