Язык JavaScript позволяет нам динамически менять свойства объектов, добавлять в объекты новые свойства и методы или удалять уже имеющиеся. Однако, подобные изменения объекта могут быть нежелательны. И JavaScript предоставляет для этого три механизма:
Запрет расширения объектов
Закрытие (sealing) объектов
Заморозка (freezing) объектов
Метод Object.preventExtensions() позволяет запретить расширение объекта, то есть в этот объект нельзя добавлять новые свойства и методы. Метод Object.preventExtensions() в качестве параметра принимает целевой объект, для которого надо установить запрет на расширение.
Сначала возьмем пример, где мы успешно добавляем новое свойство:
const tom = {name: "Tom"}; // добавляем в объект tom новое свойство - company tom.company = "Localhost"; console.log(`Name: ${tom.name} Company: ${tom.company}`); // Name: Tom Company: Localhost
Здесь в объект tom добавляется новое свойство company. После добавления мы можем использовать это свойство.
Теперь запретим расширение, применив метод Object.preventExtensions():
const tom = {name: "Tom"}; Object.preventExtensions(tom); // запрещаем расширение объекта tom tom.company = "Localhost"; // пытаемся добавить в объект tom новое свойство console.log(`Name: ${tom.name} Company: ${tom.company}`); // Name: Tom Company: undefined
В итоге даже если мы попытаемся определить для объекта новое свойство, оно не будет добавлено. А при попытке обратиться к подобному свойству мы получим undefined
Иногда может возникнуть необходимость определить, является ли объект расширяемым. Например, если объект расширяем, мы можем добавить в его свойства и затем использовать эти свойства. Для проверки расширяемости можно использовать метод Object.isExtensible(). В этот метод передается тестируемый объект. И если объект поддерживает расширение, то метод возвращает true, иначе возвращается false:
const tom = {name: "Tom"}; console.log(Object.isExtensible(tom)); // true Object.preventExtensions(tom); // запрещаем расширение объекта tom console.log(Object.isExtensible(tom)); // false
Закрытие или "запечатывание" объектов (sealing) также позволяет запретить расширение объектов. Но кроме того, также запрещает настройку уже существующих свойств. Для закрытия объектов применяется метод Object.seal().
Сначала посмотрим, что мы можем сделать с объектом без применения Object.seal():
const tom = {name: "Tom"}; // для свойства name запрещаем изменение Object.defineProperty(tom, "name", { writable: false}); tom.name = "Tomas"; // добавляем новое свойство - age tom.age = 39; console.log(`Name: ${tom.name} Age: ${tom.age}`); // Name: Tom Age: 39 // для свойства name разрешаем изменение Object.defineProperty(tom, "name", { writable: true}); tom.name = "Tomas"; console.log(`Name: ${tom.name} Age: ${tom.age}`); // Name: Tomas Age: 39
Итак, мы можем изменить конфигурацию свойства (здесь делаем свойство name недоступным для записи). И также мы можем добавить в объект новое свойство.
Теперь применим метод Object.seal():
const tom = {name: "Tom"}; Object.seal(tom); // закрываем объект tom от расширения и изменения конфигурации // для свойства name запрещаем изменение Object.defineProperty(tom, "name", { writable: false}); tom.name = "Tomas"; // добавляем новое свойство - age tom.age = 39; console.log(`Name: ${tom.name} Age: ${tom.age}`); // Name: Tom Age: undefined // для свойства name разрешаем изменение Object.defineProperty(tom, "name", { writable: true}); // Uncaught TypeError: Cannot redefine property: name
После закрытия объекта методом Object.seal(tom)
мы не сможем добавить в объект новое свойство. Соответственно в примере выше свойство
tom.age
будет равно undefined
. И также мы не сможем повторно изменить конфигурацию свойства.
Так, здесь при втором вызове метода Object.defineProperty()
для свойства name мы столкнемся с ошибкой
"Uncaught TypeError: Cannot redefine property: name".
Для проверки, является ли объект закрытым, мы можем использовать метод Object.isSealed() - если объект закрыт, метод возвращает true
. Стоит отметить, что поскольку закрытый объект нерасширяем, то метод Object.isExtensible() возвращает для него false
:
const tom = {name: "Tom"}; console.log(Object.isExtensible(tom)); // true console.log(Object.isSealed(tom)); // false Object.seal(tom); // закрываем объект tom console.log(Object.isExtensible(tom)); // false console.log(Object.isSealed(tom)); // true
Заморозка или freezing позволяет запретить изменение значений свойств, то есть позволяет сделать объект в полной мере константным. Так, просто определить объект как обычную константу с помощью оператора const недостаточно. Например:
const tom = {name: "Tom"}; tom.name= "Tomas"; console.log(tom.name); // Tomas
Здесь мы видим, что свойство объекта изменило свое значение, хотя объект определен как константа.
Оператор const
лишь влияет на то, что мы не можем присвоить константе новое значение, например, как в следующем случае:
const tom = {name: "Tom"}; tom = {name: "Sam"}; // Ошибка - нельзя константе присвоить значение второй раз
Тем не менее значения свойств объекта мы можем изменять.
Чтобы сделать объект действительно константным, необходимо применить специальный метод Object.freeze(). В этот метод в качестве параметра передается объект, который надо сделать константным:
const tom = {name: "Tom"}; Object.freeze(tom); tom.name= "Tomas"; // значение свойства нельзя изменить console.log(tom.name); // Tom
Для проверки, можно ли изменить значения свойств объекта, применяется метод Object.isFrozen() - если значения свойств изменить нельзя, он возвращает true
Следует отметить, что "замороженный" объект - это крайняя степень запрета изменений на объекте. Соответственно такой объект нерасширяем, и также нельзя изменить конфигурацию его свойств:
const tom = {name: "Tom"}; console.log(Object.isExtensible(tom)); // true console.log(Object.isSealed(tom)); // false console.log(Object.isFrozen(tom)); // false Object.freeze(tom); console.log(Object.isExtensible(tom)); // false console.log(Object.isSealed(tom)); // true console.log(Object.isFrozen(tom)); // true