JavaScript метод freeze()

JavaScript Object

Определение и применение

JavaScript метод freeze() позволяет сделать объект нерасширяемым (предотвращает добавление новых собственных (неунаследованных) свойств), устанавливает все его собственные свойства как ненастраиваемые (предотвращает их удаление и изменение дескриптора), а также устанавливает все его собственные свойства недоступными для записи (изменение свойства объекта с помощью оператора присваивания не допускается).


Обратите внимание на то, что действие этого метода необратимо, так как нерасширяемый объект с ненастраиваемыми свойствами нельзя вновь сделать расширяемым объектом.


Под ненастраиваемыми свойствами стоит понимать те, атрибут configurable которых имеет значение false, в этом случае удаление такого свойства из содержащего его объекта, а также в дальнейшем изменение дескриптора этого свойства запрещается. Атрибуты в свою очередь описываются дескрипторами свойств – это обычные объекты JavaScript, которые описывают атрибуты и значение свойства. Подробную информацию о дескрипторах свойств и атрибутах вы можете получить в описании метода defineProperty(), который позволяет определить новое или изменить существующее свойство объекта, описывая его дескрипторами.

Если в цепочке прототипов объекта на котором был вызван метод freeze() содержится расширяемый и настраиваемый объект, то в этом случае остается возможность добавлять и удалять наследуемые им свойства.

Метод freeze() дополнительно изменяет значение атрибута writable для каждого собственного (неунаследованного) свойства на значение false, благодаря чему изменение собственных свойств объекта с помощью оператора присваивания не допускается. Это является его основным отличием от метода seal(), который не проводит подобных изменений.

Чтобы проверить является ли объект нерасширяемым с недоступными для настройки и изменения свойствами, вы можете воспользоваться методом isFrozen().

Сравнение подобных методов

МетодОбъект становится нерасширяемымАтрибут configurable для каждого собственного (неунаследованного) свойства имеет значение false Атрибут writable для каждого собственного (неунаследованного) свойства имеет значение false
preventExtensions()ДаНетНет
seal()ДаДаНет
freeze()ДаДаДа

Поддержка браузерами

МетодChrome
Chrome
Firefox
Firefox
Opera
Opera
Safari
Safari
Internet Explorer
IExplorer
Microsoft Edge
Edge
freeze()6.04.012.05.19.0Да

JavaScript синтаксис:

Object.freeze( obj );

Версия JavaScript

ECMAScript 5.1 (реализовано в JavaScript 1.8.5)

Значения параметров

ПараметрОписание
objЦелевой объект, который "замораживается". Обязательное значение.

Исключения

Тип исключенияОписание
TypeErrorВозникает в том случае, если значение параметра, определяющее целевой объект не является объектом. Начиная с версии ECMAScript 6 параметр, который не является объектом будет сначала приведён к объекту.

Пример использования

// инициализируем переменную, содержащую объект
let obj = {
    a: 1,
    b: 2
};

obj.c = 3;  // добавляем новое свойство в объект

// "замораживаем" объект
Object.freeze( obj );

console.log( obj ); // {a: 1, b: 2, c: 3}

delete obj.c; // пытаемся удалить свойство (false)
obj.c = 100;  // пытаемся изменить значение свойства
obj.d = 4;    // пытаемся добавить новое свойство

console.log( obj ); // {a: 1, b: 2, c: 3}

// пытаемся изменить в объекте свойство данных
Object.defineProperty( obj, "c", { 
    // устанавливаем, что удаление свойства допускается, а также допускается изменение дескриптора этого свойства 
    configurable: true  // TypeError
}); 

В этом примере мы инициализировали переменную, содержащую объект, и с помощью метода freeze() мы "заморозили" объект. После этого мы пытаемся удалить, изменить, добавить новое свойство, или изменить дескриптор, в результате чего все наши попытки заканчиваются неудачей.

В следующем примере мы рассмотрим как "заморозить" массив:

// инициализируем переменную, содержащую массив
let arr = [1, 2, 3];

// "замораживаем" массив
Object.freeze( arr );

arr.shift();   // пытаемся удалить первый элемент массива
arr[0] = 5;    // пытаемся изменить первый элемент массива
arr.push( 4 ); // пытаемся добавить элемент к массиву

console.log( arr ); // [1, 2, 3]

В этом примере мы инициализировали переменную, содержащую массив, и с помощью метода freeze() мы его "заморозили". После этого мы пытаемся с помощью метода shift() удалить первый элемент массива, изменить первый элемент массива и с помощью метода push() добавить новый элемент в конец массива. Как вы можете убедиться все наши попытки закончились неудачей.

Замораживаемый объект является неизменяемым (иммутабельным), но это утверждение не всегда верно, в следующем примере мы рассмотрим почему замороженный объект не является константой:

// инициализируем переменную, содержащую объект
let myObj = {
  nestedObj: {}
};

// "замораживаем" объект
Object.freeze( myObj );

// задаем вложенном объекту новое свойство и значение
myObj.nestedObj.newProp = "ooops";

console.log( myObj.nestedObj.newProp ); // "ooops"

В этом примере мы инициализировали переменную, содержащую объект, и с помощью метода freeze() мы его "заморозили". После этого мы пытаемся добавить новое свойство и его значение во вложенный объект, что у нас собственно и получается. Это связано с тем, что замораживание является неглубоким (shallow).

Чтобы получить иммутабельный объект Вам необходимо, чтобы все прямые и косвенные ссылки на другие объекты ссылались только на неизменяемые замороженные объекты. Для этого необходимо рекурсивно "заморозить" каждое свойство, которое имеет тип object. Давайте рассмотрим простой пример:

// создаем функцию для глубокой "заморозки" объекта
function deepFreeze( obj ) {
  // воз­вра­ща­ем массив, состоящий из всех имен собственных (неунаследованных) свойств
  let propNames = Object.getOwnPropertyNames( obj ); 

  // "замораживаем" все свойства переданного объекта
  propNames.forEach(function( name ) {
    let prop = obj[name];

    // "замораживаем" свойство если оно явлется объектом и не равно null
    if (typeof prop == "object" && prop !== null)
      deepFreeze( prop );
  });

  // "замораживаем" переданный объекта
  return Object.freeze( obj );
}

// инициализируем переменную, содержащую объект
let myObj = {
  nestedObj: {}
};

// "замораживаем" объект и его свойства с помощью созданной функции
deepFreeze( myObj );

// задаем вложенном объекту новое свойство и значение
myObj.nestedObj.newProp = "ooops";

console.log( myObj.nestedObj.newProp ); // undefined

В этом примере мы рассмотрели как произвести глубокую "заморозку" объекта, но учтите тот момент, что объекты могут быть сложнее и, например, содержать ссылки друг на друга, что может привести к бесконечным циклам при попытке их "заморозить", или привести к заморозке таких объектов как window. В каждом конкретном случае необходимо подходить отдельно к решению той, или иной задачи.

JavaScript Object