JavaScript метод freeze()
JavaScript ObjectОпределение и применение
JavaScript метод freeze() позволяет сделать объект нерасширяемым (предотвращает добавление новых собственных (неунаследованных) свойств), устанавливает все его собственные свойства как ненастраиваемые (предотвращает их удаление и изменение дескриптора), а также устанавливает все его собственные свойства недоступными для записи (изменение свойства объекта с помощью оператора присваивания не допускается).
Обратите внимание на то, что действие этого метода необратимо, так как нерасширяемый объект с ненастраиваемыми свойствами нельзя вновь сделать расширяемым объектом.
Под ненастраиваемыми свойствами стоит понимать те, атрибут configurable которых имеет значение false, в этом случае удаление такого свойства из содержащего его объекта, а также в дальнейшем изменение дескриптора этого свойства запрещается. Атрибуты в свою очередь описываются дескрипторами свойств – это обычные объекты JavaScript, которые описывают атрибуты и значение свойства. Подробную информацию о дескрипторах свойств и атрибутах вы можете получить в описании метода defineProperty(), который позволяет определить новое или изменить существующее свойство объекта, описывая его дескрипторами.
Если в цепочке прототипов объекта на котором был вызван метод freeze() содержится расширяемый и настраиваемый объект, то в этом случае остается возможность добавлять и удалять наследуемые им свойства.
Метод freeze() дополнительно изменяет значение атрибута writable для каждого собственного (неунаследованного) свойства на значение false, благодаря чему изменение собственных свойств объекта с помощью оператора присваивания не допускается. Это является его основным отличием от метода seal(), который не проводит подобных изменений.
Чтобы проверить является ли объект нерасширяемым с недоступными для настройки и изменения свойствами, вы можете воспользоваться методом isFrozen().
Сравнение подобных методов
Метод | Объект становится нерасширяемым | Атрибут configurable для каждого собственного (неунаследованного) свойства имеет значение false | Атрибут writable для каждого собственного (неунаследованного) свойства имеет значение false |
---|---|---|---|
preventExtensions() | Да | Нет | Нет |
seal() | Да | Да | Нет |
freeze() | Да | Да | Да |
Поддержка браузерами
Метод | Chrome | Firefox | Opera | Safari | IExplorer | Edge |
---|---|---|---|---|---|---|
freeze() | 6.0 | 4.0 | 12.0 | 5.1 | 9.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