Last week, I wrote about property descriptors in JavaScript. I mentioned that you can use the static Object.defineProperty()
method to change the configuration of a property, e.g. whether it’s writable
and/or configurable
. Today, I’ll cover three further methods that help prevent changes to an object.
Object.preventExtensions()
The static Object.preventExtensions()
method prevents the addition of new properties on an object. It also prevents the object’s prototype from being reassigned. However, existing properties can still be reassigned and deleted.
In this example, we can still reassign dog.name
and we can still delete dog.bark
. However, attempting to assign dog.breed
(a new property) causes an error to be thrown in strict mode. Attempting to set the prototype of the object referenced by dog
causes an error to be thrown regardless of strict mode.
let dog = Object.preventExtensions({
name: "Rex",
bark() {
return "Woof!";
},
});
dog.name = "Spot"; // can still change values
delete dog.bark; // can still delete properties
dog.breed = "Springer Spaniel"; // throws an error in strict mode
class Dog {
constructor(name) {
this.name = name;
}
bark() {
return "Woof!";
}
}
Object.setPrototypeOf(dog, Dog.prototype); // throws an error
You can use the accompanying method, Object.isExtensible()
, to check if an object is extensible.
if (Object.isExtensible(dog)) {
// do something if the object referenced by `dog` is extensible...
}
Object.seal()
In addition to preventing extensions, the static Object.seal()
method makes an object’s existing properties non-configurable. This means their configuration can’t be changed with the Object.defineProperty()
method and they can’t be deleted. A writable property can still be made read-only, however.
In this example, dog.name
is writable, so we can still make it read-only. Attempting to delete dog.bark
causes an error to be thrown in strict mode. Attempting to change the configuration of dog.name
causes an error to be thrown regardless of strict mode.
let dog = Object.seal({
name: "Rex",
bark() {
return "Woof!";
},
});
Object.defineProperty(dog, "name", {
writable: false,
}); // can still change from writable to read-only
delete dog.bark; // throws an error in strict mode
Object.defineProperty(dog, "name", {
enumerable: false,
}); // throws an error
You can use the accompanying method, Object.isSealed()
, to check if an object is sealed.
if (Object.isSealed(dog)) {
// do something if the object referenced by `dog` is sealed...
}
Object.freeze()
In addition to preventing extensions and configuration, the static Object.freeze()
method makes an object’s existing properties read-only. This is the strictest method of the three. However, it only performs a shallow freeze, so nested objects can still be changed. You would need to recursively call the Object.freeze()
method to get a deep freeze.
In this example, we can still set a property on dog.bark
because it’s assigned to an object of type Function
. However, attempting to reassign dog.name
causes an error to be thrown.
let dog = Object.freeze({
name: "Rex",
bark() {
return "Woof!";
},
});
dog.bark.foo = "bar"; // ok because it's only a shallow freeze
dog.name = "Spot"; // throws an error in strict mode
You can use the accompanying method, Object.isFrozen()
, to check if an object is frozen.
if (Object.isFrozen(dog)) {
// do something if the object referenced by `dog` is frozen...
}
Summary
Next week, I’ll share a useful application of the Object.freeze()
method. Until then, here’s a summary of what we covered today.
- The
Object.preventExtensions()
method prevents the addition of new properties on an object. - The
Object.seal()
method also makes an object’s existing properties non-configurable and prevents their deletion. - The
Object.freeze()
method also makes an object’s existing properties read-only. However, it only performs a shallow freeze.