> Как сделать объект немутабельным в JavaScript (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Сбер

Стек: JavaScript

> Пример ответа

Короткий ответ

Немутабельность в JavaScript достигается комбинацией: Object.freeze() для поверхностной защиты, глубокого замораживания для вложенных структур, и иммутабельных паттернов (copy-on-write) через spread-оператор или structuredClone. Для примитивов немутабельность уже встроена. Важно помнить, что const не делает объект немутабельным - он лишь запрещает переприсваивание переменной.

Подробное объяснение

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

1. Object.freeze(obj) - делает объект нерасширяемым, свойства становятся неперезаписываемыми и неперечисляемыми. Но это поверхностная заморозка: вложенные объекты остаются мутабельными.

2. Object.seal(obj) - запрещает добавление/удаление свойств, но позволяет изменять значения существующих.

3. Object.preventExtensions(obj) - только запрещает добавление новых свойств.

4. Глубокая заморозка - рекурсивный Object.freeze() для всех вложенных объектов.

5. Иммутабельные структуры данных - библиотеки вроде Immutable.js или Immer, которые создают новые объекты при каждом изменении.

6. Паттерн copy-on-write - вместо мутации создаётся копия с изменениями через spread или structuredClone.

Для примитивов (string, number, boolean, null, undefined, symbol, bigint) немутабельность гарантирована языком - любые операции возвращают новое значение.

На практике

В реальных проектах чаще используют комбинацию подходов:

  • Для конфигураций и констант - Object.freeze() + глубокое замораживание.
  • Для state-менеджмента (Redux, Zustand) - иммутабельные обновления через spread или Immer.
  • Для передачи данных между потоками - structuredClone для глубоких копий.
  • Для защиты API от случайных мутаций - TypeScript Readonly<T> на уровне типов.

Важно: заморозка не защищает от мутаций через Proxy или Reflect.set, и не делает объект сериализуемым быстрее.

Пример кода

JAVASCRIPT
// Поверхностная заморозка
const config = Object.freeze({ apiUrl: 'https://api.example.com', retries: 3 });
config.retries = 5; // не сработает в strict mode, молча проигнорируется в sloppy
console.log(config.retries); // 3
// Глубокая заморозка
function deepFreeze(obj) {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object' && obj[key] !== null) {
deepFreeze(obj[key]);
}
});
return Object.freeze(obj);
}
const settings = deepFreeze({
theme: { color: 'dark', font: { size: 14 } }
});
settings.theme.font.size = 20; // не сработает
// Иммутабельное обновление через spread
const state = { user: { name: 'Alice', age: 30 } };
const newState = {
...state,
user: { ...state.user, age: 31 }
};
// structuredClone для глубокого копирования
const original = { nested: { value: 1 } };
const cloned = structuredClone(original);
cloned.nested.value = 2;
console.log(original.nested.value); // 1

Как отвечать на собеседовании

Начните с определения немутабельности и её важности для предсказуемости кода. Затем перечислите инструменты: Object.freeze(), Object.seal(), preventExtensions(), глубокое замораживание, иммутабельные паттерны. Обязательно упомяните ограничения: поверхностность freeze, невозможность полной защиты от Proxy, и что const - это не про немутабельность. Приведите пример из реальной практики - например, почему в Redux нельзя мутировать state. Если спросят про производительность, отметьте trade-off: иммутабельность даёт безопасность и упрощает отслеживание изменений, но требует больше памяти на копии.

Что проверяет интервьюер

Интервьюер оценивает:

  • Понимание разницы между const и немутабельностью.
  • Знание уровней защиты объектов (freeze, seal, preventExtensions).
  • Осознание ограничений поверхностной заморозки.
  • Умение применять иммутабельные паттерны на практике.
  • Понимание, когда нужна глубокая заморозка, а когда достаточно copy-on-write.
  • Знание современных инструментов: structuredClone, Immer, Immutable.js.

Типичные ошибки

  • Утверждение, что const делает объект немутабельным.
  • Использование Object.freeze() без учёта вложенных объектов.
  • Забывают, что замороженный объект всё ещё можно изменить через Object.defineProperty с configurable: true (если свойство было таким до заморозки).
  • Путаница между Object.freeze() и Object.seal().
  • Попытка заморозить Date, Map, Set - они не становятся полностью немутабельными через freeze.
  • Игнорирование strict mode: в sloppy mode мутация замороженного объекта молча игнорируется, что маскирует ошибки.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью