Making objects Immutable with freeze(), seal(), & preventExtensions()
If you have missed the first part of this article, then check it out here, which goes deep into property descriptors that forms the backbone in this article as well for Object.seal() and Object.freeze().
Object.preventExtensions()
Use:
It just prevents the addition of new properties for an object permanently.
var myObj = {key1: 10};Object.preventExtensions(myObj);myObj.key2 = "new prop";console.log(myObj); // {key1: 10}
How it works:
This method makes the internal property [[prototype]] of myObj immutable, which makes any new assignment to prototype of myObj impossible, hence we cannot add new properties.
NOTE: In Javascipt literatures double square brackets [[]], represent internal properties so prototype is a internal property, and internal properties are never directly exposed to user.
We also have a cool built-in function Object.isExtensible() to find out whether an object is currently extensible or not.
var myObj = { key1: 10 };console.log(Object.isExtensible(myObj)); // trueObject.preventExtensions(myObj);console.log(Object.isExtensible(myObj)); // false
Pro tips:
There is a catch, we can still add new properties up the prototype chain which will appear for myObj too, so Object.preventExtensions() is not foolproof. In order to see that we must first know how to find the prototype of its parent in the prototype chain therefore we ll use Object.getPrototypeOf() method.
var myObj = { key1: 10 };Object.preventExtensions(myObj);// Now here we already know that myObj is instance of Objectconsole.log(myObj instanceof Object); // true// Therefore its parent prototype should be of Objectvar parentPrototype = Object.getPrototypeOf(myObj);console.log(parentPrototype === Object.prototype); // true
Now we are sure that myObj is derived from Object, so adding new properties to the prototype of Object will add new properties to myObj as well.
var myObj = { key1: 10 };Object.preventExtensions(myObj);Object.prototype.key2 = "new prop";console.log(myObj.key2); // "new prop"
Object.seal()
Use:
It is used to perform the following operation on objects
- Prevent addition of new properties just like preventExtensions().
- Prevents deletion of properties.
- Prevents modifications of property descriptors (except writable true to false).
var myObj = { key1: 10};Object.seal(myObj);// New properties cant be added to myObj since its sealedmyObj.key2 = "new prop";
console.log(myObj.key2); // undefined// Existing properties cant be deleted from a sealed objectdelete myObj.key1; // Throws TypeError in strict mode// Descriptors of existing properties cant be modifiedObject.defineProperty(myObj, "key1", {enumerable: false});
// TypeError: Cannot redefine property: key1
How it works:
* To prevent addition of new properties it makes the object non-extensible just like preventExtensions() above by making its [[prototype]] immutable.
* To prevent deletion of properties and modification of descriptors it makes all the properties in the object non-configurable.
var myObj = { key1: 10 };console.log(Object.getOwnPropertyDescriptor(myObj, "key1"));
/*{ value: 10,
writable: true,
enumerable: true,
configurable: true }*/Object.seal(myObj);console.log(Object.getOwnPropertyDescriptor(myObj, "key1"));
/*{ value: 10,
writable: true,
enumerable: true,
configurable: false }*/
We also have a cool built-in function Object.isSealed() to find out whether an object is currently sealed or not.
var myObj = { key1: 10 };console.log(Object.isSealed(myObj)); // falseObject.seal(myObj);console.log(Object.isSealed(myObj)); // true
Pro tips:
Just like in Object.preventExtensions() new properties can still be added to a sealed object by adding properties higher up in the prototype chain.
var myObj = { key1: 10 };Object.seal(myObj);Object.prototype.key2 = "new prop";console.log(myObj.key2); // "new prop"
Object.freeze()
Uses:
Its the most restrictive and it does what the above two does plus it makes the properties non-writable so that existing properties cant be modified.
- Prevents addition of new properties just like preventExtensions().
- Prevents deletion of existing properties.
- Prevents modifications of property descriptors (except writable true to false).
- Prevents the modification of existing property values.
var myObj = { key1: 10};Object.freeze(myObj);// New properties cant be added to myObj since its frozenmyObj.key2 = "new prop"; // Throws TypeError in strict mode
console.log(myObj.key2); // undefined// Existing properties cannot be deleteddelete myObj.key1; // Throws TypeError in strict mode// Descriptors of existing properties cant be modifiedObject.defineProperty(myObj, "key1", {enumerable: false});
// Throws TypeError: Cannot redefine property: key1// Cannot modify existing propertiesmyObj.key1 = 20; // Throws TypeError in strict mode
console.log(myObj.key1); // 10
How it works:
* To prevent addition of new properties it makes the object non-extensible just like Object.preventExtensions() above by making its [[prototype]] immutable.
* To prevent deletion of properties and modification of descriptors it makes all the properties in the object non-configurable.
* To prevent the existing properties from modifications it makes all the properties non-writable.
var myObj = { key1: 10 };console.log(Object.getOwnPropertyDescriptor(myObj, "key1"));
/*{ value: 10,
writable: true,
enumerable: true,
configurable: true }*/Object.freeze(myObj);console.log(Object.getOwnPropertyDescriptor(myObj, "key1"));
/*{ value: 10,
writable: false,
enumerable: true,
configurable: false }*/
Just like the above two we also have a built-in function Object.isFrozen() to find out whether an object is currently frozen or not.
var myObj = { key1: 10 };console.log(Object.isFrozen(myObj)); // falseObject.freeze(myObj);console.log(Object.isFrozen(myObj)); // true
Pro Tips:
Just like in Object.preventExtensions() and Object.seal(), new properties can still be added to a frozen object by adding properties higher up in the prototype chain.
var myObj = { key1: 10 };Object.freeze(myObj);Object.prototype.key2 = "new prop";console.log(myObj.key2); // "new prop"
Correlation among the 3 methods
A frozen object is both non-extensible and sealed.
var myObj = { key1: 10 };Object.freeze(myObj);console.log(Object.isExtensible(myObj)); // false
console.log(Object.isSealed(myObj)); // true
console.log(Object.isFrozen(myObj)); // true
A sealed object is non-extensible but not frozen.
var myObj = { key1: 10 };Object.seal(myObj);console.log(Object.isExtensible(myObj)); // false
console.log(Object.isSealed(myObj)); // true
console.log(Object.isFrozen(myObj)); // false
Just a non-extensible object is neither sealed nor frozen.
var myObj = { key1: 10 };Object.preventExtensions(myObj);console.log(Object.isExtensible(myObj)); // false
console.log(Object.isSealed(myObj)); // false
console.log(Object.isFrozen(myObj)); // false
This table clearly denotes the correlation among the three
To get an even clearer picture and remember that freeze incorporates other two and seal incorporates preventExtensions this diagram serves us best.
References:
- Freezing, Sealing & Preventing Extensions: By LucasF Costa
- Mozilla Docs