Object.freeze()

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

Синтаксис

Object.freeze(obj)

Параметри

obj
Об'єкт, який потрібно заморозити.

Значення, що повертається

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

Опис

Ніщо не може бути додане чи видалене з набору властивостей замороженого об'єкта. Будь-яка спроба це зробити зазнає невдачі, або непомітно, або з викиданням винятку TypeError (найчастіше, але не винятково, у строгому режимі).

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

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

Приклади

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

var obj = {
  prop: function() {},
  foo: 'ква'
};

// До заморожування: можна додавати нові властивості,
// а також змінювати чи видаляти існуючі властивості
obj.foo = 'мяв';
obj.lumpy = 'гав';
delete obj.prop;

// Заморожуємо.
var o = Object.freeze(obj);

// Повертається той самий об'єкт, який ми передали.
o === obj; // true

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

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

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

fail();

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

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

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

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

a[0]=1; // непомітно не спрацьовує
a.push(2); // непомітно не спрацьовує

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

fail();

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

obj1 = {
  internal: {}
};

Object.freeze(obj1);
obj1.internal.a = 'значенняА';

obj1.internal.a // 'значенняА'

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

Що таке "неглибока заморозка"?

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

var employee = {
  name: "Дмитро",
  designation: "Розробник",
  address: {
    street: "Городоцька",
    city: "Львів"
  }
};

Object.freeze(employee);

employee.name = "Вова"; // непомітно не спрацює у нестрогому режимі
employee.address.city = "Винники"; // атрибути дочірнього об'єкта можна змінювати

console.log(employee.address.city) // Виведе: "Винники"

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

function deepFreeze(object) {

  // Отримати імена властивостей, визначених на об'єкті
  var propNames = Object.getOwnPropertyNames(object);

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

    object[name] = value && typeof value === "object" ? 
      deepFreeze(value) : value;
  }

  return Object.freeze(object);
}

var obj2 = {
  internal: {
    a: null
  }
};

deepFreeze(obj2);

obj2.internal.a = 'новеЗначення'; // непомітно не спрацює у нестрогому режимі
obj2.internal.a; // null

Примітки щодо використання

У ES5, якщо аргументом цього методу є не об'єкт (примітив), він спричинить помилку TypeError. У ES2015 аргумент, який не є об'єктом, сприйматиметься як звичайний заморожений об'єкт, і буде просто повернений.

> Object.freeze(1)
TypeError: 1 is not an object // код ES5

> Object.freeze(1)
1                             // код ES2015

Об'єкти ArrayBufferView з елементами спричинять помилку 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.seal(), дозволяють змінювати свої існуючі властивості. Властивості об'єктів, заморожених через Object.freeze(), стають незмінними.

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

Специфікація Статус Коментар
ECMAScript 5.1 (ECMA-262)
The definition of 'Object.freeze' in that specification.
Standard Початкове визначення. Реалізоване у JavaScript 1.8.5.
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Object.freeze' in that specification.
Standard
ECMAScript Latest Draft (ECMA-262)
The definition of 'Object.freeze' in that specification.
Draft

Сумісність з веб-переглядачами

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
freezeChrome Full support 6Edge Full support 12Firefox Full support 4IE Full support 9Opera Full support 12Safari Full support 5.1WebView Android Full support YesChrome Android Full support 18Firefox Android Full support 4Opera Android Full support YesSafari iOS Full support YesSamsung Internet Android Full support Yesnodejs Full support Yes

Legend

Full support  
Full support

Див. також