CodingBison

JavaScript enables us to control object-level behavior as well. For this purpose, it provides a handful of built-in methods: Object.preventExtensions(), Object.seal(), and Object.freeze(). All of these methods accept the object that needs to be modified as an argument.

Let us describe these three methods in more detail.

Method Object.preventExtensions() disables the ability to add any new property for that object; in terms of attributes, it switches off the extensible attribute of the object.

Method Object.seal() is the next step -- not only does it disable the ability to add a new property, it also renders the object un-configurable; in terms of attributes, it switches off the extensible attribute of the object and also switches off the configurable attribute of each property.

Method Object.freeze() is the most stringent among all the three. It completely locks the object; in terms of attributes, it switches off the extensible attribute of the object and also switches off the writable and configurable attributes of each property. The only degree of freedom offered by Object.freeze() is that the properties are left enumerable; this way, we get to at least to loop over the properties!

To test if these attributes have been set, JavaScript provides yet another three set of methods: Object.isExtensible(), Object.isSealed() and Object.isFrozen(). If these constraints are set, then these methods return true for respective constraints; else, they return false. All of these methods accept the object that needs to be tested as an argument.

Next, we provide three simple examples that dive into behavior of the above three methods of Object.preventExtensions(), Object.seal(), and Object.freeze().

The first example focuses on Object.preventExtensions() method and then uses Object.isExtensible() to confirm if the desired constraint has been put in place or not.

 <!doctype html>
 <html>
 <div id="idDiv"></div>
 <script type="text/javascript">
 // Get a handle of the div element.
 var elem = document.getElementById("idDiv");

 // Return property descriptors of the passed property.
 function getPropAttributes (obj, prop) {
     var x = "", i = 0;
     var attr = Object.getOwnPropertyDescriptor(obj, prop);
     for (var item in attr) {
         if (i++ != 0) {x += ", ";}
         x += (item + ": " + attr[item]);
     }
     return x;
 }

 // Trivial function to print an object.
 function printObject (obj) {
     elem.innerHTML += "Printing objFable: <br>";
     for (var item in obj) {
         elem.innerHTML += item + " -- "+ getPropAttributes(obj, item) + "<br>";
     }
     elem.innerHTML += "Extensible Attribute: " + Object.isExtensible(obj) + "<br><br>";
 }

 // Define an object.
 var objFable = {name: "Farmer Giles of Ham", dragon: "Chrysophylax"};

 // Let us print property descriptors along with object's extensible attribute.
 printObject(objFable);

 // Prevent future extensions.
 Object.preventExtensions(objFable);

 // Let us try to extend the object.
 objFable.author = "J. R. R. Tolkien";

 // Let us (re)print property descriptors along with object's extensible attribute.
 printObject(objFable);
 </script>
 </html>

The output (provided below) shows that after invoking Object.preventExtensions() method, when we try to add a property to that object, then JavaScript silently ignores that attempt and does not add the new property. Understandably, when we try to print properties of the object, then the attempted property is not listed since it was not added successfully. Note that Object.preventExtensions() modifies only the extensible attribute of the object and the property-level attributes of object's properties remain unchanged.



Figure: Object.preventExtensions() Method

The second example focuses on Object.seal(). Basically, Object.seal() offers a similar functionality as that of Object.preventExtensions(), but, in addition, it also casts one more constraint on its top -- it makes each object property un-configurable!

The example (provided below) first seals the object and then uses all the three test-related methods to show the current state of the object: Object.isExtensible(), Object.isSealed(), and Object.isFrozen().

 <!doctype html>
 <html>
 <div id="idDiv"></div>
 <script type="text/javascript">
 // Get a handle of the div element.
 var elem = document.getElementById("idDiv");

 // Return property descriptors of the passed property.
 function getPropAttributes (obj, prop) {
     var x = "", i = 0;
     var attr = Object.getOwnPropertyDescriptor(obj, prop);
     for (var item in attr) {
         if (i++ != 0) {x += ", ";} 
         x += (item + ": " + attr[item]);
     }   
     return x;
 }

 // Trivial function to print an object.
 function printObject (obj) {
     elem.innerHTML += "Printing objFable: <br>";
     for (var item in obj) {
         elem.innerHTML += (item + " -- "+ getPropAttributes(obj, item) + "<br>");
     }
     elem.innerHTML += "Extensible Attribute: " + Object.isExtensible(obj);
     elem.innerHTML += "<br>IsSealed: " + Object.isSealed(obj);
     elem.innerHTML += "<br>IsFrozen: " + Object.isFrozen(obj) + "<br><br>";
 }

 // Define an object.
 var objFable = {name: "Farmer Giles of Ham", dragon: "Chrysophylax"};

 // Let us print objFable.
 printObject(objFable);

 // Time to seal the object!
 Object.seal(objFable);

 // Let us try to update/extend the object. 
 objFable.author = "J. R. R. Tolkien";
 objFable.dragon = "Smaug"; 

 // Let us (re)print objFable. 
 printObject(objFable);
 </script>
 </html>

When we seal the object, the configurable flag is turned off. However, other attributes are not touched. Thus, we can neither configure attributes of any properties nor we can extend (add new properties) the object. If the writable attribute is true, then we can certainly modify the value of a property.



Figure: Object.seal() Method

The third example focuses on Object.freeze(). Object.freeze() is rather stringent. Except for enumerable attribute of properties, it switches off all the attributes (both object-level and property-level). Thus, when we freeze an object, all we can do is loop through its properties! Note that once we seal and (or) freeze an object, there is no way to revert the object back to normal state since now, the object is rendered un-configurable. This is a good design since if the application logic creates a global object with a set of attributes, then freezing it implies that users of this object can merely read it. Also, since Object.freeze() is very stringent, once we freeze an object, we automatically also seal the object.

This example (provided below) first freezes an object and then uses the test-related methods to contrast the state of the object before and after we freeze the object.

 <!doctype html>
 <html>
 <div id="idDiv"></div>
 <script type="text/javascript">
 // Get a handle of the div element.
 var elem = document.getElementById("idDiv");

 // Return property descriptors of the passed property.
 function getPropAttributes (obj, prop) {
     var x = "", i = 0;
     var attr = Object.getOwnPropertyDescriptor(obj, prop);
     for (var item in attr) {
         if (i++ != 0) {x += ", ";} 
         x += (item + ": " + attr[item]);
     }   
     return x;
 }

 // Trivial function to print an object.
 function printObject (obj) {
     elem.innerHTML += "Printing objFable: <br>";
     for (item in obj) {
         elem.innerHTML += (item + " -- "+ getPropAttributes(obj, item) + "<br>");
     }
     elem.innerHTML += "Extensible Attribute: " + Object.isExtensible(obj);
     elem.innerHTML += "<br>IsSealed: " + Object.isSealed(obj);
     elem.innerHTML += "<br>IsFrozen: " + Object.isFrozen(obj) + "<br><br>";
 }

 // Define an object.
 var objFable = {name: "Farmer Giles of Ham", dragon: "Chrysophylax"};

 // Let us print objFable. 
 printObject(objFable);

 // Time to freeze the object!
 Object.freeze(objFable);

 // Let us try to update/extend the object.
 objFable.author = "J. R. R. Tolkien";
 objFable.dragon = "Smaug"; 

 // Let us (re)print objFable.
 printObject(objFable);
 </script>
 </html>

And, here is the output of the above example. We see that after we freeze the object, all of its attributes (object-level and property-level), except enumerable, are turned off.



Figure: Object.freeze() Method

Thus, we can use the above methods to tighten object properties and thus, if needed, we can create immutable objects.

Please note that we do not have to explicitly call Object.seal() or Object.freeze() to provide object-level constraints; we can also use Object.defineProperty() and Object.preventExtensions() to do the same!

If we start with an object and set configurable attribute for all of its properties to false (using Object.defineProperty()) and set extensible attribute to false (using Object.preventExtensions()), then the object would automatically become sealed; for such an object, calling Object.isSealed() would return true.

Likewise, if we start with an object and set both writable and configurable of all properties to false (using Object.defineProperty()) and set extensible attribute to false (using Object.preventExtensions()), then the object would automatically becomes frozen; for such an object, calling Object.isFrozen() would return true.





comments powered by Disqus