Each property of an object has three attributes: writable, enumerable, and configurable. JavaScript provides two methods to modify these attributes: Object.defineProperty() and Object.defineProperties(). As is evident from the method name, the first method enables us to modify attributes of a single property. The second method enables us to modify attributes of multiple properties in one-shot. To test each of these properties, we can use the earlier Object.getOwnPropertyDescriptor() and it will return a list of all of these attributes.
In addition, to test if a property is enumerable, JavaScript provides a special method: propertyIsEnumerable(). Thus, to test if property "dragon" of object "objFable" is enumerable or not, we can call: objFable.propertyIsEnumerable(""dragon""); this method will return true if the property is enumerable, else it will return false. Note that JavaScript does not provide analogous methods of propertyIsWritable() and propertyIsConfigurable().
To explore these attributes, we provide three examples, each focusing on one of the three attributes.
The first example focuses on the enumerable attribute of a property. This example shows that if the enumerable attribute is set to false, then the propertyIsEnumerable() method returns false and that property does not show up in a for/in loop.
The example (provided below) uses Object.defineProperty() to switch off the enumerable attribute of the "dragon" property of the "objFable" object. Since the for/in loop skips non-enumerable properties, when we reprint the object properties, we find that it does not print the "dragon" property of objFable.
<!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 += "<br>"; } // Define an object. var objFable = {name: "Farmer Giles of Ham", dragon: "Chrysophylax"}; // Print objFable before setting property attributes. printObject(objFable); // Additionally, check if the "dragon" property is enumerable. elem.innerHTML += "propertyIsEnumerable: " + objFable.propertyIsEnumerable("dragon"); elem.innerHTML += "<br><br>"; // Set property descriptors for the property "dragon" to non-enumerable. Object.defineProperty(objFable, "dragon", {enumerable:false}); // Print objFable again after setting the property attribute. printObject(objFable); // Check again, if the property is enumerable. elem.innerHTML += "propertyIsEnumerable: " + objFable.propertyIsEnumerable("dragon"); </script> </html>
Here is the output of when we load the above example (save the example as file "objects_attribute_enumerable.png" and point the browser to it).
With Object.defineProperty(), if the property specified does not exist in the object, then the method adds that property; same holds if we specify multiple properties with Object.defineProperties(). Thus, we can use these two methods to even build an object from scratch.
Regarding the attributes parameter, we do not have to specify the attribute parameter or parameters for Object.defineProperty() and Object.defineProperties(). However, the behavior is different depending upon if the property is new or existing. For a new property, if we do not provide any of these parameters, then the attributes are defaulted to false. For an existing property, if we do not provide any of these parameters, then the attributes are defaulted to the current value of those property attributes.
If we are interested only in enumerable properties, then besides a for/in loop, we can also use the Object.keys() method. Thus, in the earlier program, "Object.keys(objFable)" would return an array '["name", "dragon"]' before we turn off the enumerable property. And, once we set the enumerable attribute to false, "Object.keys(objFable)" would return an array '["name"]'.
The second example focuses on the writable attribute of a property and shows that if we set the writable property to false, then we cannot modify the property value.
This example (provided below) uses Object.defineProperties() to define objFable object and to specify its values and attributes and its sets the writable attribute to false. Next, we try to assign new values to each of these properties, then JavaScript ignores it since they are not writable. When we re-print the object, the property values remain unchanged.
<!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 += "<br>"; } // We can use Object.defineProperties() to even define an object! var objFable = Object.defineProperties({}, { "name": {value: "The Hobbit", writable: false, enumerable: true, configurable: true}, "dragon": {value: "Smaug", writable: false, enumerable: true, configurable: true}, }); // Let us print objFable. printObject(objFable); // Attempt to (unsuccessfully) change the values of these properties. objFable.name = "Farmer Giles of Ham"; objFable.dragon = "Chrysophylax"; // Reprint objFable; since writable is false, we would not see any changes. elem.innerHTML += "Printing objFable (just name/value pairs): <br>"; for (var item in objFable) {elem.innerHTML += (item + " -- "+ objFable[item] + "<br>");} </script> </html>
Here is the output of when we load the above example (save the example as file "attribute_writable.png" and point the browser to it).
The third and the last example focuses on the configurable attribute of a property and shows that if we set this property to false, then we cannot configure that property any longer.
The example (provided below) redefines the "objFable" object and sets the configurable attribute as false for its properties. When we try to set the writable to true, then JavaScript throws an error since the configurable attribute is set to false. Therefore, once we turn off the configurable attribute, there is no way to configure the configurable attribute off. In other words, once we turn off the configurable attribute, there is no way to turn it on!
<!doctype html> <html> <div id="idDiv"></div> <script type="text/javascript"> // Get a handle of the div element. var elem = document.getElementById("idDiv"); // We can use Object.defineProperties() to even define an object! var objFable = Object.defineProperties({}, { "name": {value: "The Hobbit", writable: false, enumerable: true, configurable: false}, "dragon": {value: "Smaug", writable: false, enumerable: true, configurable: false}, }); // Now, let us print objFable elem.innerHTML += "[Before Setting] Printing objFable object: <br>"; for (var item in objFable) {elem.innerHTML += item + ": " + objFable[item] + "<br>";} try {Object.defineProperty(objFable, "name", {writable: true}); } catch (err) {elem.innerHTML += "<br>Error description: " + err.description;} try {Object.defineProperty(objFable, "dragon", {writable: true}); } catch (err) {elem.innerHTML += "<br>Error description: " + err.description + "<br>";} // Let us try to (unsuccessfully) change the values of these properties objFable.name = "Farmer Giles of Ham"; objFable.dragon = "Chrysophylax"; // Now, let us print objFable elem.innerHTML += ("<br>[After Setting] Printing objFable object: <br>"); for (var item in objFable) {elem.innerHTML += item + ": " + objFable[item] + "<br>";} </script> </html>
We provide the output of this example below. The output confirms that when we try to configure the writable attribute to true (it is false in the beginning), then JavaScript throws an error since the property is non-configurable. An additional proof that the step of configuring writable to true fails comes when when we try to update the property values. Since the writable attribute was not set to true, the updated values are not reflected when we try to re-print the object.