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

Как работает Object.freeze?

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

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

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

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

Как работает Object.freeze() в JavaScript

Object.freeze() - это метод, который делает объект неизменяемым. Он предотвращает добавление, удаление и изменение свойств объекта. Это важный инструмент для обеспечения immutability в функциональном программировании.

1. Базовое использование

const user = {
  name: 'Alice',
  age: 30,
  email: 'alice@example.com'
};

// Заморозить объект
Object.freeze(user);

// Попытка изменения - молча не срабатывает (в strict mode выбросит ошибку)
user.age = 31;  // Не изменится
user.name = 'Bob'; // Не изменится

// Попытка добавления нового свойства
user.phone = '123456'; // Не добавится

// Попытка удаления
delete user.email; // Не удалится

console.log(user); // { name: 'Alice', age: 30, email: 'alice@example.com' }

// Проверка, заморожен ли объект
console.log(Object.isFrozen(user)); // true

2. Strict Mode и Object.freeze

'use strict';

const obj = { count: 0 };
Object.freeze(obj);

try {
  obj.count = 1; // TypeError: Cannot assign to read only property 'count'
} catch (e) {
  console.error(e.message);
}

Без strict mode ошибок нет, но присваивание не срабатывает (молчаливо игнорируется).

3. Глубокое замораживание (Deep Freeze)

Обратите внимание: Object.freeze() замораживает только первый уровень:

const user = {
  name: 'Alice',
  address: {
    city: 'New York',
    country: 'USA'
  }
};

Object.freeze(user);

// Это не сработает
user.address = { city: 'London', country: 'UK' };

// Но это сработает! Вложенный объект не заморожен
user.address.city = 'London';

console.log(user); // { name: 'Alice', address: { city: 'London', country: 'UK' } }

// Функция для глубокого замораживания
function deepFreeze(obj) {
  Object.freeze(obj);
  
  Object.getOwnPropertyNames(obj).forEach(prop => {
    if (obj[prop] !== null &&
        (typeof obj[prop] === 'object' || typeof obj[prop] === 'function') &&
        !Object.isFrozen(obj[prop])) {
      deepFreeze(obj[prop]);
    }
  });
  
  return obj;
}

const safeUser = deepFreeze(user);
user.address.city = 'Paris'; // Теперь это не сработает

4. Object.freeze vs Object.seal vs Object.preventExtensions

// 1. Object.freeze() - не можно менять, добавлять, удалять
const frozen = { a: 1 };
Object.freeze(frozen);
frozen.a = 2;      // Ошибка
frozen.b = 3;      // Ошибка
delete frozen.a;   // Ошибка

// 2. Object.seal() - можно менять, но не добавлять/удалять
const sealed = { a: 1 };
Object.seal(sealed);
sealed.a = 2;      // OK
sealed.b = 3;      // Ошибка
delete sealed.a;   // Ошибка

// 3. Object.preventExtensions() - можно менять/удалять, но не добавлять
const extended = { a: 1 };
Object.preventExtensions(extended);
extended.a = 2;    // OK
extended.b = 3;    // Ошибка
delete extended.a; // OK

// Таблица:
// Операция         | freeze | seal | preventExtensions
// Изменение        | Нет    | Да   | Да
// Добавление       | Нет    | Нет  | Нет
// Удаление         | Нет    | Нет  | Да

5. React и Object.freeze для оптимизации

import { useMemo } from 'react';

function UserList({ users }) {
  // Object.freeze помогает React оптимизировать
  const frozenUsers = useMemo(() => {
    return Object.freeze(users);
  }, [users]);
  
  return (
    <ul>
      {frozenUsers.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

// Freeze для constants
const API_ENDPOINTS = Object.freeze({
  users: '/api/users',
  posts: '/api/posts',
  comments: '/api/comments'
});

// Попытка изменить не сработает
API_ENDPOINTS.users = '/api/modified'; // Игнорируется

6. Object.freeze для защиты конфигурации

const config = Object.freeze({
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retries: 3,
  features: Object.freeze({
    darkMode: true,
    notifications: true
  })
});

// Ошибка при попытке изменения
config.timeout = 10000; // Молчаливо игнорируется

// Правильный способ - создать новый объект
const newConfig = {
  ...config,
  timeout: 10000
};

// Или для вложенных
const newConfig2 = {
  ...config,
  features: {
    ...config.features,
    darkMode: false
  }
};

7. Object.freeze vs const

// const предотвращает переприсваивание переменной
const obj = { count: 0 };
obj = { count: 1 }; // TypeError: obj is read-only

// Но не предотвращает изменение свойств
obj.count = 1; // OK

// Object.freeze предотвращает изменение свойств
const frozen = { count: 0 };
frozen.count = 1; // Не срабатывает

// Комбо: const + Object.freeze = максимальная защита
const config = Object.freeze({
  app: 'MyApp',
  version: '1.0.0'
});

config.app = 'NewApp'; // Не срабатывает
config = {}; // TypeError: config is read-only

8. Проверка свойств замороженного объекта

const user = { name: 'Alice', age: 30 };
Object.freeze(user);

// Получение дескриптора свойства
const descriptor = Object.getOwnPropertyDescriptor(user, 'name');
console.log(descriptor);
// { value: 'Alice', writable: false, enumerable: true, configurable: false }

// Все свойства читаются
console.log(Object.getOwnPropertyNames(user)); // ['name', 'age']

// Проверка на наличие свойства
console.log('name' in user); // true
console.log(user.hasOwnProperty('name')); // true

9. Object.freeze с методами

const calculator = Object.freeze({
  value: 0,
  add(n) {
    return calculator.value + n; // Чтение OK
  },
  multiply(n) {
    return calculator.value * n;
  }
});

// Методы работают, но не могут менять состояние
console.log(calculator.add(5)); // 5

// Это не сработает
calculator.value = 10; // Игнорируется

10. Производительность и альтернативы

// Object.freeze имеет overhead
const largeObject = Object.freeze({
  // 10000 свойств
});

// Для приватных данных используй WeakMap/WeakSet
const privateData = new WeakMap();

class User {
  constructor(name) {
    privateData.set(this, { name });
  }
  
  getName() {
    return privateData.get(this).name;
  }
}

// Или используй # private fields (ES2022)
class SecureUser {
  #name;
  
  constructor(name) {
    this.#name = name;
  }
  
  getName() {
    return this.#name;
  }
}

Ключевые моменты

  • Object.freeze() делает объект неизменяемым на уровне свойств
  • Замораживает только первый уровень (используй deepFreeze для вложенных)
  • В strict mode выбросит TypeError при попытке изменения
  • Без strict mode ошибок нет, но изменения не произойдут
  • Object.isFrozen() проверяет, заморожен ли объект
  • Отличается от const (const - для переменной, freeze - для объекта)
  • Используй для конфигов, констант и защиты от случайного изменения
  • Для более сложной инкапсуляции используй private fields (#)
Как работает Object.freeze? | PrepBro