← Назад к вопросам

Какие знаешь иммутирующие функции?

2.0 Middle🔥 122 комментариев
#JavaScript Core

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Иммутирующие функции в JavaScript

Иммутирующие (immutable) функции — это функции, которые не изменяют исходные данные, а возвращают новые объекты или новые структуры данных с примененными изменениями. Этот подход критически важен для:

  • Предсказуемости состояния
  • Отладки и отслеживания изменений
  • Работы с React, Redux и другими библиотеками
  • Функционального программирования

Основные категории иммутирующих функций

1. Для работы с массивами

// Исходный массив НЕ изменяется
const originalArray = [1, 2, 3, 4, 5];

// Возвращает новый массив
const newArray = originalArray.map(x => x * 2);
const filtered = originalArray.filter(x => x > 2);
const sliced = originalArray.slice(0, 2);
const concated = originalArray.concat([6, 7]);

// ES6+ оператор spread
const withSpread = [...originalArray, 6];
const inserted = [...originalArray.slice(0, 2), 2.5, ...originalArray.slice(2)];

2. Для работы с объектами

const originalObject = { a: 1, b: 2, c: 3 };

// Object.assign (осторожно — поверхностное копирование)
const newObj1 = Object.assign({}, originalObject, { d: 4 });

// Оператор spread для объектов
const newObj2 = { ...originalObject, b: 20 };
const deepUpdate = {
  ...originalObject,
  nested: { ...originalObject.nested, value: 'updated' }
};

// Для удаления свойств
const { b, ...withoutB } = originalObject;

3. Специальные библиотеки и подходы

// Immutable.js
import { List, Map } from 'immutable';
const immutableList = List([1, 2, 3]);
const newList = immutableList.push(4);

// Immer (популярный современный подход)
import produce from 'immer';
const nextState = produce(originalState, draft => {
  draft.user.age = 25;
  draft.items.push({ id: 3 });
});

// Ручное глубокое копирование
const deepClone = JSON.parse(JSON.stringify(originalObject));

Почему иммутабельность важна?

Преимущества:

  • Упрощение отслеживания изменений — каждый объект уникален
  • Простота сравнения — можно использовать простое равенство ссылок
  • Потокобезопасность — нет гонок данных
  • Откат изменений — предыдущие состояния сохраняются
  • React оптимизации — чистые компоненты (PureComponent) и React.memo работают эффективнее

Практические паттерны:

  • В Redux редьюсеры всегда должны быть чистыми функциями
  • В React обновление состояния через setState с новыми объектами
  • Использование useReducer для сложных состояний

Пример комплексного использования

// Иммутабельное обновление сложной структуры
const updateUserProfile = (state, userId, updates) => {
  return {
    ...state,
    users: state.users.map(user => 
      user.id === userId 
        ? { ...user, ...updates, lastUpdated: Date.now() }
        : user
    ),
    metadata: {
      ...state.metadata,
      lastModified: Date.now(),
      modifiedBy: 'system'
    }
  };
};

// Создание иммутабельной функции для специфической операции
const immutableUpdateInArray = (array, index, updater) => {
  return [
    ...array.slice(0, index),
    typeof updater === 'function' ? updater(array[index]) : updater,
    ...array.slice(index + 1)
  ];
};

Производительность и оптимизация

Иммутабельность создает новые объекты, что может быть накладным для производительности. Решения:

  • Структурное разделение (structural sharing) в библиотеках типа Immutable.js
  • Мемоизация дорогих вычислений
  • Селекторы для производных данных
  • Пейджинг и ленивая загрузка при работе с большими данными

На практике я комбинирую нативные иммутирующие методы JavaScript с Immer для сложных обновлений и lodash/fp для функциональных операций. Выбор конкретного подхода зависит от размера приложения, требований к производительности и команды разработчиков.