Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как заморозить объект
Замораживание объекта — это техника, которая делает объект неизменяемым. В JavaScript есть несколько способов разной глубины и функциональности. Расскажу про все подходы.
1. Object.freeze() — самый простой способ
const user = { name: 'Alice', age: 30 };
Object.freeze(user);
// Пытаюсь изменить
user.age = 31; // В strict mode: TypeError, в обычном режиме молча игнорируется
user.email = 'alice@example.com'; // То же самое
delete user.name; // Не работает
console.log(user); // { name: 'Alice', age: 30 } — не изменился
// Проверяю, заморожен ли объект
console.log(Object.isFrozen(user)); // true
Что происходит при freeze:
- Нельзя добавлять свойства
- Нельзя удалять свойства
- Нельзя изменять существующие свойства
- Нельзя переконфигурировать property descriptors
Проблема: это shallow freeze!
const user = {
name: 'Alice',
address: { city: 'NYC' }
};
Object.freeze(user);
// Попытка изменить top-level
user.name = 'Bob'; // Не работает
// Но вложенный объект не заморожен!
user.address.city = 'LA'; // РАБОТАЕТ!
console.log(user); // { name: 'Alice', address: { city: 'LA' } }
2. Object.seal() — более мягкий способ
const user = { name: 'Alice', age: 30 };
Object.seal(user);
// Можно МЕНЯТЬ существующие свойства
user.age = 31; // РАБОТАЕТ
// Но нельзя ДОБАВЛЯТЬ новые
user.email = 'alice@example.com'; // Не работает
// И нельзя УДАЛЯТЬ
delete user.age; // Не работает
console.log(user); // { name: 'Alice', age: 31 } — возраст изменился
console.log(Object.isSealed(user)); // true
Отличие от freeze:
- freeze: нельзя читать и писать
- seal: можно писать, но нельзя добавлять/удалять ключи
3. Object.preventExtensions() — самый мягкий способ
const user = { name: 'Alice' };
Object.preventExtensions(user);
// Можно менять существующие свойства
user.name = 'Bob'; // РАБОТАЕТ
// Можно удалять
delete user.name; // РАБОТАЕТ
// Но нельзя добавлять новые
user.age = 30; // Не работает
console.log(user); // { name: 'Bob' }
console.log(Object.isExtensible(user)); // false
4. Deep freeze — полное замораживание вложенных объектов
function deepFreeze(obj) {
// Сначала замораживаем сам объект
Object.freeze(obj);
// Затем рекурсивно замораживаем все вложенные объекты
Object.values(obj).forEach(value => {
if (typeof value === 'object' && value !== null) {
deepFreeze(value);
}
});
return obj;
}
const user = {
name: 'Alice',
address: {
city: 'NYC',
country: { name: 'USA' }
},
hobbies: ['reading', 'coding']
};
deepFreeze(user);
// Попытка изменить вложенный объект
user.address.city = 'LA'; // Не работает
// Попытка изменить массив
user.hobbies[0] = 'gaming'; // Не работает
user.hobbies.push('sports'); // Не работает
console.log(user); // Всё осталось как было
5. Property Descriptors — полный контроль
const user = {};
// Добавляю свойство с полным контролем
Object.defineProperty(user, 'age', {
value: 30,
writable: false, // Нельзя изменить
enumerable: true, // Видно в for...in
configurable: false // Нельзя переконфигурировать
});
// Пытаюсь изменить
user.age = 31; // Не работает (в strict mode — ошибка)
delete user.age; // Не работает (configurable: false)
// Пытаюсь переконфигурировать
Object.defineProperty(user, 'age', { value: 31 }); // TypeError!
console.log(user.age); // 30
Различные combinations:
const config = {};
// Read-only свойство
Object.defineProperty(config, 'apiKey', {
value: 'secret-key-123',
writable: false,
configurable: false
});
// Скрытое свойство (не видно в for...in)
Object.defineProperty(config, 'internalId', {
value: 'internal-123',
enumerable: false,
writable: false
});
// Свойство с getter/setter
Object.defineProperty(config, 'theme', {
get() {
return this._theme || 'light';
},
set(value) {
if (['light', 'dark'].includes(value)) {
this._theme = value;
}
},
enumerable: true,
configurable: true
});
console.log(config.theme); // 'light'
config.theme = 'dark'; // OK
config.theme = 'invalid'; // Игнорируется
6. Использование в React
// Чтобы убедиться, что state не мутируется
const initialState = Object.freeze({
users: [],
loading: false,
error: null
});
function reducer(state = initialState, action) {
switch (action.type) {
case 'SET_USERS':
// Нельзя случайно заморозить state
// state.users = action.payload; // TypeError если в strict mode
// Правильный способ — вернуть новый объект
return { ...state, users: action.payload };
case 'SET_LOADING':
return { ...state, loading: action.payload };
default:
return state;
}
}
7. Проверка уровня заморозки
const obj = { a: 1 };
const sealedObj = { b: 2 };
const normalObj = { c: 3 };
Object.freeze(obj);
Object.seal(sealedObj);
// Проверка статуса
console.log(Object.isFrozen(obj)); // true
console.log(Object.isSealed(obj)); // true (frozen объект тоже sealed)
console.log(Object.isExtensible(obj)); // false
console.log(Object.isSealed(sealedObj)); // true
console.log(Object.isFrozen(sealedObj)); // false
console.log(Object.isExtensible(sealedObj)); // false
console.log(Object.isExtensible(normalObj)); // true
8. Практический пример: конфигурация
const CONFIG = Object.freeze({
API_URL: 'https://api.example.com',
TIMEOUT: 5000,
MAX_RETRIES: 3,
FEATURES: Object.freeze({
AUTH: true,
PAYMENTS: false,
NOTIFICATIONS: true
})
});
// Попытка изменить конфигурацию
CONFIG.API_URL = 'https://attacker.com'; // Не работает
CONFIG.FEATURES.PAYMENTS = true; // Не работает
// Конфигурация безопасна
console.log(CONFIG); // Оригинальные значения
9. Производительность
// Замораживание имеет минимальный overhead
const obj = { a: 1, b: 2 };
console.time('freeze');
for (let i = 0; i < 1000000; i++) {
Object.freeze({ x: 1 });
}
console.timeEnd('freeze');
// freeze: ~50ms (на современных браузерах)
// Доступ к frozen объектам может быть немного быстрее
// (браузер знает, что объект не изменится)
const frozen = Object.freeze({ x: 1 });
const mutable = { x: 1 };
console.time('frozen access');
for (let i = 0; i < 100000000; i++) {
frozen.x;
}
console.timeEnd('frozen access');
// В некоторых случаях замороженный доступ быстрее благодаря оптимизации
10. Когда использовать
Object.freeze():
- Конфигурации (не должны меняться)
- Константы (объекты вместо примитивов)
- Immutable паттерны в Redux
- Когда нужна полная защита от мутации
Object.seal():
- Когда объект может менять существующие свойства
- Но новые свойства не должны добавляться
- Реже используется
Object.preventExtensions():
- Когда только нужно предотвратить добавление новых ключей
- Очень редко
defineProperty():
- Когда нужен полный контроль
- Read-only флаги
- Computed properties (getters/setters)
- Скрытые свойства
11. Ограничения
// freeze НЕ защищает от:
const obj = Object.freeze({ arr: [1, 2, 3] });
// Изменение вложенного массива
obj.arr[0] = 999; // РАБОТАЕТ!
console.log(obj); // { arr: [999, 2, 3] }
// Решение: deep freeze
function deepFreeze(obj) {
Object.freeze(obj);
Object.values(obj).forEach(v => {
if (typeof v === 'object' && v !== null) {
deepFreeze(v);
}
});
return obj;
}
const obj2 = deepFreeze({ arr: [1, 2, 3] });
obj2.arr[0] = 999; // Не работает
Сравнительная таблица
| Метод | Добавлять свойства | Менять свойства | Удалять свойства | Глубина |
|---|---|---|---|---|
| freeze | Нет | Нет | Нет | Shallow |
| seal | Нет | Да | Нет | Shallow |
| preventExtensions | Нет | Да | Да | Shallow |
| deepFreeze | Нет | Нет | Нет | Deep |
| defineProperty | Контроль | Контроль | Контроль | Single property |
Ключевой вывод
Для замораживания объекта:
-
Object.freeze() — для полной неизменяемости
const obj = Object.freeze({ ... }); -
Object.seal() — если нужно менять существующие свойства
Object.seal(obj); -
Object.preventExtensions() — только если нужно предотвратить добавление
Object.preventExtensions(obj); -
deepFreeze() — для вложенных объектов
function deepFreeze(obj) { ... } -
Object.defineProperty() — для максимального контроля
Object.defineProperty(obj, 'key', { ... });