Язык 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