JavaScript Powerful Property Descriptors

We developers assume that an object’s property is only a container that can be assigned a name and a value. In actuality though, JavaScript gives us a series of powerful property descriptors that further shape how the property behaves. To demonstrate this, let's create a JavaScript object by using object literal

  1. var student = {  
  2. rollno: 1,  
  3. name: 'Ajay'  
  4. }  

Now, let's take a look at the property descriptor for the name property of our student object.

  1. var nameDescriptor = JSON.stringify(Object.getOwnPropertyDescriptor(student,'name'));  
  2. document.write(nameDescriptor);  

JSON.stringify is used to print the JavaScript object returned by Object.getOwnPropertyDescriptor function.

It will print {"value": "Ajay" ,"writable":true,"enumerable":true,"configurable":true}

So we can see that in addition to the name property having a value, it also has writable, enumerable, and configurable attributes. And these are all set to true by default.

We will go deep down in these properties to see what they're used for and how we can change them.

Using the Writable Attribute

The writable attribute does what you would probably expect. It defines whether the value associated with the property can be changed from its initial value. So let's make the name property non-writable. We can do this by using the Object.defineProperty method like this.

  1. Object.defineProperty(student, 'name', { writable: false });  

So let's see what happens if we try to change the name of our student object.

  1. student.name = "Ross";  

We can see the unchanged value for the name property by printing it out

  1. document.write(student.name); // it will print Ajay  

If you are running your JavaScript code in strict mode by putting 'use strict'; at the top of your JavaScript file. Then, you can also see the error thrown by a writable descriptor in your browser console window.

Now, let's take a look at what happens if a non-writable property contains an object. So, let's change the value of our name to an object, and that object will have a first and a last name like this.

  1. var student = {  
  2. rollno: 1,  
  3. name: {first: 'Ajay', last: 'Mor'}  
  4. }  
  5.   
  6. Object.defineProperty(student, 'name', { writable: false });  

If we try to set it to another object or value then it will fail and throw an error as we saw in the previous example.

  1. student.name = {first: 'Jon', last: 'Smith'}; //won't work  

But here's the interesting thing. We actually can change the object that is pointed to by that property like this.

  1. student.name.first = 'Jon';  
  2.   
  3. document.write(student.name.first);  

So we can see that even though the name property was read-only, we were able to change the value of a property of the object that the name property was pointing to. It perfectly makes sense when you think about it since the name is really just a pointer. And when you make it read-only, you're only preventing that pointer from being changed.

What if, we don't want to let that happen. We actually can prevent the object from being changed by using Object.freeze like this.

  1. Object.freeze(student.name);  

If you try changing value again, they will remain unchanged because the entire name object is now read-only in addition to the actual name property. So just keep that in mind if we ever make a property read-only that if the property contains an object, we'll need to freeze the object also in order to prevent it from being changed.

 Using the Enumerable Attribute

First, let us see how we can iterate over properties and their values in an object.

  1. var student = {  
  2. rollno: 1,  
  3. name: 'Ajay'  
  4. }  
  5.   
  6. for(var propertyName in student){  
  7. document.writeln(propertyName + ' : '+ student[propertyName] + '<br/>');  

By default, properties on an object are enumerable. If we set them to false then we cannot iterate over those properties. So, let's make enumerable false for the name property.

  1. Object.defineProperty(student,'name',{ enumerable:false });  
  2.   
  3. for(var propertyName in student){  
  4. document.writeln(propName + ' : '+ student[propName] + '<br/>');  
  5. }  

Now, notice that even though we are looping over all the properties in student object but the name property was not actually returned in the loop.

That is one of the main use cases for the enumerable property. Some other use cases are given below

If we go and see object properties using Object.keys() method

  1. document.write(Object.keys(student))  

then it will only show up those properties which are set to true for an enumerable attribute.

Another, which I found very interesting is setting enumerable to false affects JSON serialization of the object.

  1. var student = {  
  2. rollno: 1,  
  3. name: 'Ajay'  
  4. }  
  5.   
  6. Object.defineProperty(student,'name',{ enumerable:false });  
  7.   
  8. document.write(JSON.stringify(student))  

As we can see that the name property is not JSON serialized. So that shows us the power of the enumerable property.

Note that we can still look at the property with bracket notation. So setting enumerable to false does not change the ability to look at the property. We just can't enumerate it, see it in the object's keys, or serialize it.

Using the Configurable Attribute

The configurable property locks down a property to prevent certain attributes from being changed. It also prevents the property from being deleted from the object.

  1. var student = { rollno: 1, name: 'Ajay' }  
  2. Object.defineProperty(student, 'name', { configurable: false });   

if we try to change the enumerable property, it won't work

  1. Object.defineProperty(student,'name',{ enumerable:false });  

It is interesting that once we have made a property non-configurable, you can't make it configurable again.

  1. Object.defineProperty(student, 'name', { configurable: false }); // won't work  

We can, however, still change the writable attribute.

  1. Object.defineProperty(student, 'name', { writable: false }); // will work  

There is one more thing that changing the configurable property does. It makes it so that you can't delete a property.

  1. var student = {name : 'ajay', age: '30', country: 'India' }  
  2. delete student.name // will throw error  

So to summarize, there are three things that are affected by making a property non-configurable.

  1. We cannot change the enumerable attribute,
  2. We cannot change the configurable attribute,
  3. We cannot delete the property.

If you enjoyed this post, I’d be very grateful if you’d help it spread by emailing it to a friend or sharing it on Twitter or Facebook. Thank you!