Сравнение методов ограничения объектов в JavaScript: что можно и нельзя делать с замороженными, запечатанными и нерасширяемыми объектами.
Ограничение объектов: preventExtensions, seal и freeze
JavaScript предоставляет три уровня «заморозки» объекта — от лёгкого запрета на добавление новых свойств до полной неизменяемости. Понимание разницы между ними помогает выбирать правильный уровень защиты данных.
В JavaScript объекты по умолчанию открыты для любых изменений: в них можно добавлять, удалять и перезаписывать свойства, а также менять их дескрипторы. Однако при разработке библиотек, работе с конфигурациями или реализации иммутабельного состояния (например, в Redux) такое поведение нежелательно. Для контроля над объектами в языке существуют три метода: Object.preventExtensions(), Object.seal() и Object.freeze().
Каждый из них накладывает определённые ограничения, причём каждый следующий является более строгим вариантом предыдущего. Разберём их по порядку, от самого мягкого к самому жёсткому.
Object.preventExtensions — запрет на расширение
Метод Object.preventExtensions(obj) запрещает добавлять новые свойства в объект. При этом все существующие свойства остаются полностью управляемыми: их можно изменять, удалять и перенастраивать их дескрипторы.
const config = { host: "localhost", port: 3000 };
Object.preventExtensions(config);
config.port = 8080; // ✅ Значение изменилось
delete config.host; // ✅ Свойство удалено
config.ssl = true; // Новое свойство не добавится
// (в strict mode — TypeError)
console.log(Object.isExtensible(config)); // false
Проверить, можно ли расширять объект, помогает метод Object.isExtensible(obj). После вызова preventExtensions вернуть объекту расширяемость уже нельзя — это односторонняя операция.
Object.seal — запечатывание объекта
Метод Object.seal(obj) делает всё, что делает preventExtensions, и дополнительно запрещает удалять существующие свойства. При этом значения свойств по-прежнему можно менять, если они были writable.
Под капотом seal устанавливает всем свойствам флаг configurable: false. Это означает, что нельзя ни удалить свойство, ни изменить его дескриптор (например, превратить обычное свойство в геттер/сеттер).
const user = { name: "Ivan", age: 25 };
Object.seal(user);
user.age = 30; // ✅ Значение изменилось
delete user.name; // ❌ Удалить нельзя
user.city = "Moscow"; // ❌ Добавить нельзя
Object.defineProperty(user, "name", {
writable: false // ❌ Изменить дескриптор нельзя
});
console.log(Object.isSealed(user)); // true
Object.freeze — полная заморозка
Метод Object.freeze(obj) — самый строгий уровень защиты. Он делает всё, что делает seal, и дополнительно запрещает изменять значения свойств. Объект становится полностью неизменяемым (иммутабельным).
Под капотом freeze устанавливает всем свойствам флаги configurable: false и writable: false.
const constants = { PI: 3.14159, E: 2.71828 };
Object.freeze(constants);
constants.PI = 3.14; // ❌ Значение не изменится
constants.NEW_PROP = 1; // ❌ Добавить нельзя
delete constants.E; // ❌ Удалить нельзя
console.log(Object.isFrozen(constants)); // true
Сводная таблица ограничений
Для наглядности все три метода можно сравнить в одной таблице. Чем ниже метод в списке, тем больше ограничений он накладывает.
| Метод | Добавление новых свойств | Удаление свойств | Изменение значений свойств | Изменение атрибутов свойств |
|---|---|---|---|---|
Object.preventExtensions |
❌ | ✅ | ✅ | ✅ |
Object.seal |
❌ | ❌ | ✅ | ❌ |
Object.freeze |
❌ | ❌ | ❌ |
Логика проста: каждый следующий метод «наследует» ограничения предыдущего и добавляет свои. Поэтому freeze — это, по сути, seal + запрет на изменение значений, а seal — это preventExtensions + запрет на удаление и изменение дескрипторов.
Важный нюанс: поверхностное действие
Все три метода действуют только на первом уровне объекта. Если свойство содержит вложенный объект или массив, он остаётся изменяемым.
const data = {
title: "Report",
meta: { author: "Ivan" }
};
Object.freeze(data);
data.title = "New"; // ❌ Не изменится
data.meta.author = "Petr"; // ✅ Изменится! Вложенный объект не заморожен
Для полной (глубокой) заморозки нужно рекурсивно обойти все вложенные объекты и применить freeze к каждому из них.
function deepFreeze(obj) {
if (obj === null || typeof obj !== "object") return obj;
Object.freeze(obj);
for (const key in obj) {
if (Object.hasOwn(obj, key)) {
deepFreeze(obj[key]);
}
}
return obj;
}
const config = deepFreeze({ db: { host: "localhost" } });
config.db.host = "remote"; // ❌ Теперь и вложенное свойство защищено
Поведение в strict mode
В обычном режиме JavaScript при нарушении ограничений просто молча игнорирует операцию — свойство не изменится, но и ошибки не будет. Это частая причина трудноуловимых багов.
В строгом режиме ("use strict") любая запрещённая операция выбрасывает TypeError. Поэтому при работе с замороженными объектами всегда рекомендуется использовать strict mode.
"use strict";
const obj = Object.freeze({ x: 1 });
obj.x = 2; // TypeError: Cannot assign to read only property 'x'
Практическое применение
preventExtensions— когда нужно зафиксировать набор свойств объекта, но оставить возможность менять их значения. Например, форма с фиксированным набором полей.seal— когда структура объекта важна, но данные внутри могут обновляться. Например, состояние сессии пользователя с фиксированным набором ключей.freeze— для констант, конфигураций, словарей и любых данных, которые не должны меняться после инициализации. Активно используется в Redux для иммутабельности state.
Что запомнить
Object.preventExtensions()запрещает только добавление новых свойств.Object.seal()запрещает добавление и удаление свойств, но разрешает изменение значений.Object.freeze()запрещает любые изменения объекта на первом уровне.- Каждый следующий метод включает ограничения предыдущего.
- Все три метода действуют поверхностно — вложенные объекты нужно замораживать отдельно.
- Операции ограничения необратимы — «разморозить» объект нельзя.
- В strict mode нарушения ограничений вызывают
TypeError, в обычном режиме — молча игнорируются. - Проверить состояние объекта помогают методы
Object.isExtensible(),Object.isSealed()иObject.isFrozen().