Object.freeze()

Метод Object.freeze() заморожує об'єкт. Замороження об'єкта перешкоджає його розширенню, і робить наявні властивості незаписуваними та неналаштовними. Заморожені об'єкти більше не можна змінювати: неможливо додати нові властивості, наявні властивості неможливо видалити, їхні перелічуваність, налаштовність, записуваність чи значення не можна змінити, і не можна повторно присвоїти прототип об'єкта. Виклик freeze() повертає той самий об'єкт, який було передано до нього.

Замороження об'єкта — це найвищий рівень цілісності, який може надати JavaScript.

Спробуйте його в дії

Синтаксис

Object.freeze(obj)

Параметри

obj

Об'єкт до замороження.

Повернене значення

Об'єкт, який було передано до функції.

Опис

Замороження об'єкта рівносильне перешкоджанню його розширенню, а потім заміні атрибута configurable дескрипторів усіх його властивостей на false – і для властивостей даних також зміна writable на false. До переліку властивостей замороженого об'єкта не можна ні додати властивості, ні прибрати наявні. Будь-яка спроба це зробити провалиться: або тихо, або з викиданням винятку TypeError (найчастіше у суворому режимі, хоча й не завжди).

Стосовно властивостей даних у заморожених об'єктів — їхні значення неможливо змінити, адже значенням їхніх атрибутів writable та configurable встановлюється false. Аксесорні властивості (гетери й сетери) працюють як і раніше — значення, повернене гетером, все ще може змінюватися, а сетер і далі зможе викликатися без викидання помилки, під час встановлення значень властивості. Слід зауважити, що ті значення властивостей, які є об'єктами, можна буде й далі змінювати, якщо вони також не заморожені. Оскільки масив також є об'єктом, його так само можна заморозити; після цього його елементи не можна змінювати, додавати та вилучати з масиву.

Приватні властивості не мають концепції дескрипторів властивостей. Замороження об'єкта з приватними властивостями не перешкоджає зміні значень цих приватних властивостей. (Замороження об'єктів зазвичай використовується як захисний захід проти зовнішнього коду, але зовнішній код все одно не може звертатися до приватних властивостей.) Приватні властивості не можна додавати чи вилучати з об'єкта, незалежно від того, чи заморожений він, чи ні.

Виклик freeze() повертає той самий об'єкт, який було передано до функції. Він не створює замороженої копії.

Примірник TypedArray чи DataView з елементами призведе до помилки TypeError, оскільки такі об'єкти є проєкціями на область пам'яті, і, авжеж, призведуть до інших можливих проблем:

Object.freeze(new Uint8Array(0)); // Без елементів
// Uint8Array []

Object.freeze(new Uint8Array(1)); // З елементами
// TypeError: Cannot freeze array buffer views with elements

Object.freeze(new DataView(new ArrayBuffer(32))); // Без елементів
// DataView {}

Object.freeze(new Float64Array(new ArrayBuffer(64), 63, 0)); // Без елементів
// Float64Array []

Object.freeze(new Float64Array(new ArrayBuffer(64), 32, 2)); // З елементами
// TypeError: Cannot freeze array buffer views with elements

Слід зазначити, що, оскільки три стандартні властивості (buf.byteLength, buf.byteOffset та buf.buffer) доступні лише для читання (так само як і аналогічні властивості примірників ArrayBuffer чи SharedArrayBuffer), — немає потреби намагатися заморозити ці властивості.

На відміну від Object.seal(), наявні в об'єктах, заморожених за допомогою Object.freeze(), властивості стають незмінними, а властивості даних — недоступними для повторного присвоєння.

Приклади

Замороження об'єктів

const obj = {
  prop() {},
  foo: "bar",
};

// До заморожування — можна додавати нові властивості,
// а наявні властивості можуть бути змінені чи видалені
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;

// Заморозити.
const o = Object.freeze(obj);

// Повернене значення – той самий об'єкт, що був переданий.
o === obj; // true

// Об'єкт став замороженим.
Object.isFrozen(obj); // === true

// Тепер будь-які зміни зазнають невдачі
obj.foo = "quux"; // мовчки не робить нічого
// мовчки не додає властивість
obj.quaxxor = "the friendly duck";

// В суворому режимі такі спроби викидатимуть помилки TypeError
function fail() {
  "use strict";
  obj.foo = "sparky"; // викидає TypeError
  delete obj.foo; // викидає TypeError
  delete obj.quaxxor; // повертає true, адже атрибут 'quaxxor' не було додано
  obj.sparky = "arf"; // викидає TypeError
}

fail();

// Спроби внести зміни за допомогою Object.defineProperty.
// Обидві наведені нижче інструкції викинуть TypeError.
Object.defineProperty(obj, "ohai", { value: 17 });
Object.defineProperty(obj, "foo", { value: "eit" });

// Також неможливо змінити прототип,
// обидві наведені нижче інструкції викинуть TypeError.
Object.setPrototypeOf(obj, { x: 20 });
obj.__proto__ = { x: 20 };

Заморожування масивів

const a = [0];
Object.freeze(a); // Тепер масив не можна змінити.

a[0] = 1; // мовчки не спрацьовує

// В суворому режимі такі спроби викидатимуть TypeError
function fail() {
  "use strict";
  a[0] = 1;
}

fail();

// Спроба заштовхати нове значення
a.push(2); // викидає TypeError

Заморожений об'єкт стає незмінним. Проте це не обов'язково означає, що він буде сталим. Наступний приклад ілюструє, що заморожений об'єкт не є сталим (тобто, замороження є поверхневим).

const obj1 = {
  internal: {},
};

Object.freeze(obj1);
obj1.internal.a = "якесь значення";

obj1.internal.a; // 'якесь значення'

Аби стати сталим об'єктом, весь граф посилань (безпосередні та опосередковані посилання на інші об'єкти) повинен посилатися виключно на незмінні заморожені об'єкти. Заморожений об'єкт називається незмінним через те, що стан всього об'єкта (значення та посилання на інші об'єкти) всередині цілого об'єкта зафіксовано. Варто зазначити, що рядки, числа, та булеві значення завжди незмінні, а функції та масиви — це об'єкти.

Глибоке заморожування

Результат виклику Object.freeze(object) застосовується лише до безпосередніх властивостей об'єкта object, і запобігає майбутнім додаванням нових властивостей, операціям видалення чи повторного присвоєння лише на цьому об'єкті. В разі, якщо ті властивості самі є об'єктами, ті об'єкти не заморожуються, і можуть бути ціллю операцій додавання або видалення властивостей, чи повторного присвоєння значення.

const employee = {
  name: "Маянк",
  designation: "Розробник",
  address: {
    street: "Рогіні",
    city: "Делі",
  },
};

Object.freeze(employee);

employee.name = "Кінь в пальто"; // мовчки не спрацьовує в несуворому режимі
employee.address.city = "Нойда"; // атрибути дочірніх об'єктів можна модифікувати

console.log(employee.address.city); // "Нойда"

Аби зробити весь об'єкт незмінним, слід рекурсивно заморозити кожну з тих його властивостей, які мають об'єктний тип (глибоке замороження). Можна застосовувати цей патерн в архітектурі, залежно від конкретного випадку, якщо відомо, що об'єкт не містить зациклень у графі залежностей, бо інакше це спричинить нескінченний цикл. Наприклад, функції, створені за допомогою запису function, мають властивість prototype із властивістю constructor, що вказує на саму функцію, тобто вони усталено мають зациклення. Інші функції, як от стрілкові, все ж можна заморозити.

Певним вдосконаленням функції deepFreeze() було б зберігати об'єкти, що вже оброблено, щоб можна було заблокувати рекурсивний виклик deepFreeze(), коли об'єкт перебуває в процесі перетворення на незмінний. Один з прикладів дивіться у використанні WeakSet для відстеження циклічних посилань. Проте залишиться ризик заморозити об'єкт, який заморожуватися не повинен, наприклад, window.

function deepFreeze(object) {
  // Отримання назв означених на об'єкті властивостей
  const propNames = Reflect.ownKeys(object);

  // Заморожування властивостей, перед заморожуванням самого себе
  for (const name of propNames) {
    const value = object[name];

    if ((value && typeof value === "object") || typeof value === "function") {
      deepFreeze(value);
    }
  }

  return Object.freeze(object);
}

const obj2 = {
  internal: {
    a: null,
  },
};

deepFreeze(obj2);

obj2.internal.a = "якесьІншеЗначення"; // мовчки не спрацьовує в несуворому режимі
obj2.internal.a; // null

Специфікації

Сумісність із браузерами

desktop mobile server
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet Deno Node.js
freeze
Chrome Full support 6
Edge Full support 12
Firefox Full support 4
Internet Explorer Full support 9
Opera Full support 12
Safari Full support 5.1
WebView Android Full support 1
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 12
Safari on iOS Full support 6
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0

Дивіться також