Property Descriptor in JavaScript

JavaScript gives us control to define, modify, and configure properties. This is made possible using a property descriptor object, which has the following attributes:

- value 

- writable 

- configurable 

- enumerable 

When we define a property on an object as follows:

var car = {

_doors: 4

};

The property descriptor's value attribute is set to 4, writable (which controls if we can write values to an object property) is set to true, configurable (which controls if we can delete a property) is set to true, and enumerable (which controls if property can be enumerated) is also set to true. We can check these attributes as follows:

var desc = Object.getOwnPropertyDescriptor (car, '_doors');

alert(desc.writable); // true

alert(desc.configurable); // true

alert(desc.value); // 4

alert(desc.enumerable); // true

Another important thing to keep in mind that Object.getOwnPropertyDescriptor works only on 'own' properties and not on 'prototype' properties. Furthermore, we can set all these property descriptor attributes using the Object.defineProperty() as follows:

Object.defineProperty (car, 'name', {

value: 'Audi',

writable: true

});

var desc = Object.getOwnPropertyDescriptor (car, 'name');

alert(car.name);

alert(desc.writable);

alert(desc.enumerable); // If you don't specify it, it defaults to false

alert(desc.configurable); // Didn't specify, so defaults to false

If we don't specify the value of attributes in Object.defineProperty(), they default to false, and that's one important thing to keep in mind. Complete code is given below:

Getter and Setter in JavaScript

We can define properties on an object as follows:

var car = {

_name: 'Audi'

};

We can also configure those properties. I will soon write about how to configure the properties (e.g., whether the property should be writable or if it should be configurable, and etc.), but we can also define "getter" and "setter" on object's properties. For example, "setter" could be useful in a scenario where we would want to do some processing on the property before setting its value to something and same goes for the "getter". So, there are two ways to go about it. One way is to define "getter" and "setter" in an object itself, e.g.

Another way is to define "getter" and "setter" by using/calling the built-in Object.defineProperty (object, property, descriptor_object). Object.defineProperty method takes three parameters:

  • Object: The object whose property we want to define or configure. 
  • Property: The property on the object that we want to define or change. 
  • Descriptor object: Descriptor object allows us to configure the property and it has following attributes, namely; writable, value, get, set, configurable, and enumerable. I am going to write more about it a little later. 


 So, we can define both "getter" and "setter" using Object.defineProperty like the following:

So that's about it, for now. Let me know what do you think. Please let me know if there are any other ways to achieve the above (or, even, please feel free to suggest corrections in this post--thanks in advance).


Defining and Configuring Object Properties in JavaScript

So JavaScript has made it possible for us to interact with property attributes. Now we can make an object property enumerable, set it to read only and even make it configurable. This is possible because of property attributes. 

Object properties and property descriptor

So object has properties and we can set the state of those properties using fields (e.g. value, writable, enumerable, get, set, and configurable) of property descriptor object. We can see how we can access fields of property descriptor object below:

So here is an object:

Let's say we wouldn't anyone to delete the chasis_no property or may be we would want engine_no as just a read-only property. By the way, all properties of an object are both enumerable and configurable by default. JavaScript allows us to interact with object properties and we can do that by calling Object.defineProperty method which takes an object, property name we want to interact with and property descriptor object as parameters. 

Data and accessor properties

There are data properties and accessor properties. Data property is a value which can be read, written to, configured and and even enumerated. Whereas accessor properties define get and set functions that are called when property is read or written to, respectively. If we look at the following code:

'wife_enabled' is the data property whereas 'name' is being used as the accessor property. 

So there are two kinds of property attributes, namely; data properties and accessor property attributes. JavaScript allows us to configure properties, let's consider few scenarios:

  • A JavaScript programmer may want to control whether a property of a certain object should be deleted or not. For example, he may not want to allow deletion of a certain property. 
  • Let's say a User class is defined with two properties; username and password. Furthermore, let's say I don't want to make the password property enumerable, that is, I don't want the password property to appear when someone is enumerating User properties.
  • Let's say a Car class has a property named chassis number and we want to make it a read only property, i.e., no one can write it. How do we go about it?

Using property attributes to change state of properties

We can make all this happen using property attributes (and they can be set using property descriptor object and we will shortly see an example), and those are:

  • [[Enumerable]]: Allows us to make a property enumerable, i.e., if it's false, then this property won't show up when the object's properties are enumerated using a for-in loop. Its default value is set to false.
  • [[Configurable]]: It defines whether the property can be deleted or if it can be changed to accessor property . If it's set to false, then property cannot be deleted and it cannot be changed to accessor property. Default value of this attribute is set to false.
  • [[Value]]: The actual value of property is stored in this. Default value of this attribute is set to undefined.
  • [[Writable]]: Allows us to configure whether we can write to a property. If it's set to false we cannot alter the property's [[Value]] attribute. So, for example, if we want to create a read only property we should set writable attribute to false. Default value of this attribute is false.
  • [[Get]]: This contains the Getter function which you can define to do some processing while reading the value. [[Get]] is exclusively used for accessor properties. Default value of this attribute is undefined.
  • [[Set]]: As the name suggests, this contains the Setter function which you can define to do some processing while setting the value. [[Set]] is exclusively used for accessor properties. Default value of this attribute is undefined.

So, both [[Enumerable]] and [[Configurable]] are common to both data and accessor properties, whereas, data properties have two additional attributes, namely; [[Value]] and [[Writable]]. Accessor properties have two more attributes and those are [[Get]] and [[Set]].

Interesting, JavaScript allows us to configure all these property attributes using a method named Object.definedProperty(object, property_name, property_descriptor). So the first parameter is the object, second is the name of the property we want to configure and third is the property descriptor that contains the values of enumerable (configures the internal property [[Enumerable]]), configurable (configures the internal property [[Configurable]]), value (configures the internal property [[Value]]), writable (configures the internal property [[Writable]]), get (configures the internal property [[Get]]) and set (configures the internal property [[Set]]) as shown below.

Hopefully, the following code will clear things up:

There, however, is one caveat we should keep in mind. If we don't specify the value of property attributes when we are trying to configure a property then they default to false. So let's look at some code.

In the above example, we have not given the values of writable, and configurable attributes when we called Object.defineProperty() so they will default to false. So this is something important which needs to be kept in mind while configuring property attributes.

References:


    Specifying prototypes using object literals in JavaScript

    In JavaScript, there are different ways to create an object. Once the object is created we can check its type by:

    our_inst instanceof InstType


    If we want to know about the function that created the instance we can do following:

    our_inst.constructor == FunctionThatCreatedIt

    So "constructor" is a property defined on the Property object and it always points back to the function that created the instance. If the object is created using object literal then following will return true because the literals (i.e. var our_inst = {}; ) point back to Object.

    our_inst.constructor == Object; 

    If the object is created using a constructor function e.g.

    function Browser(name, version){
      this.name = name;
      this.version = version;

    }

    Then following will return true (as chrome.constructor should point back to the Browser):

    var chrome = new Browser("Chrome", "1.0.3");

    alert(chrome.constructor==Browser);


    So far so good. Now inheritance in JavaScript works using a "prototype" property, which in itself is an interesting topic. If you add properties to the "prototype" then it will be accessible to all instances of that type. We can add multiple "prototypes" to our type like following (and whatever we add to the prototype, it will be available to all instances of Browser type):


    Browser.prototype = {

      renderText: function(){

        return alert('Rendering Text...');

      },

      renderCSS:function(){

        return alert("Rendering CSS...");

      }

    }; 


    But now if I do the following, it will return 'false':

    alert(chrome.constructor==Browser);


    Which looks quite strange, no? When I came across this I was not sure what happened there. So there are few things at play here, let's look at them one by one.


    1- Every function in JavaScript has a property named 'prototype' which points to the Prototype object. The Prototype object has a property named "constructor" which always points back to the constructor function that created the instance. 

    2- Now if you try to define the prototype like 

    Browser.prototype = {};

    Then, what happens is that we have assigned an object literal to Browser's prototype. So the "constructor" is defined on the Prototype object but now we have assigned an object literal to Browser's prototype and object literal's "constructor" point backs to "Object". Since the object literal we assigned doesn't have any 'constructor' property so it defaults back to the Object. So that's why following outputs "Object" as opposed to "Browser".


    alert(chrome.constructor==Browser);


    To remedy this situation we can explicitly set the 'constructor' property inside the object literal like:

    Browser.prototype = {

      constructor: Browser,

      renderText: function(){

        return alert('Rendering Text...');

      },

      renderCSS:function(){

        return alert("Rendering CSS...");

      }

    }; 

    I am no JavaScript czar, I just love learning about it. If you find any mistakes or errors, please reach out to me so I can correct those. Following Gist contains all the corrected code.