Because of the dynamic nature of JavaScript, people are able to modify freely build-in objects (a technique known as monkey-patching) and change their behavior, which can eventually cause different side-effects and bugs.
In ECMAScript 3 an object property definition consisted only of value, name and context. There was no way to manage the mutable state of a property.
ECMAScript 5 introduced a new mechanisms for managing and restriction of what can be done to an object member. A new .defineProperty() and .defineProperties() methods were implemented. As you can most certainly guess, they define new object properties, just like the dot-notation property definition with one big difference - these methods can control the characteristics of the property. By characteristics I mean the ability to change the value of the property (writable), the ability to change the characteristics of the property once defined (configurable) and the ability to determines whether or not the property is enumerated (shown) with all of the other members (enumerable).
It simply means that the property will show up if you iterate over the object using for..in loop or Object.keys. Let's look at one simple example:
const obj = {
purposeOfLife: 42,
}
Object.getOwnPropertyDescriptor(obj, 'purposeOfLife')
Object.getOwnPropertyDescriptor() returns an object with the characteristics of the property. In our case it will return:
{
value: 42,
writable: true,
enumerable: true,
configurable: true
}
As you can see enumerable is set to true. This is because every new property is enumerable by default (enumerable set to true). There is an exception to this rule, but we will look at it later.
Now, let's extend your object with one more property:
obj.favouriteWebsite = 'Hashnode'
Object.getOwnPropertyDescriptor(obj, 'favouriteWebsite ')
// {
// value: "favouriteWebsite ",
// enumerable: true,
// writable: true,
// configurable: true
// }
As you can see, the enumerable flag is set to true by default. But what if we want to create a new property which is not enumerable. We can do this with the help of the Object.defineProperty() method:
Object.defineProperty(obj, 'flameWarTopic', {
value: 'Angular2 vs ReactJS',
configurable: true,
writable: false,
enumerable: false
});
Now we have a new property named flameWarTopic with a value of "Angular2 vs ReactJS". To check if an object's property is enumerable or not, we can use the build-in Object method propertyIsEnumerable():
const isEnumerable = obj.propertyIsEnumerable('flameWarTopic') // false
There is one really important point you need to remember - properties, created with defineProperty() method have an enumerable flag set to false by default if not explicitly set to true.
This is were the things get a little bit messy. The defineProperty() method accepts 3 parameters:
The descriptor object is where we define the characteristics of the property. Let's see what happens if you don't explicitly set the enumerable flag:
Object.defineProperty(obj, 'last', {
value: '',
configurable: true
});
const lastEnumerable = obj.propertyIsEnumerable('last'); // false
In the example above,lastEnumerable just returns false. This is because by default, values added using Object.defineProperty() are immutable.
The only thing left is to see an example of the for..in loop, so here it is:
for (let key in obj) {
console.log(key)
}
The result will be the following:
"purposeOfLife"
"favoriteWebsite"
Our obj have 4 properties - purposeOfLife, favouriteWebsite, flameWarTopic and last, but only the enumerable properties will show up.
I've combined the examples in a single jsbin, which you can check and experiment with - examples.