JavaScript Property Flags and Descriptors

Objects are capable of storing properties. Unlike a regular property, an object property is considered a more powerful and flexible thing. In this chapter, we are going to represent to you several additional options for configuration.

Property Flags

Object properties include three specific attributes (besides the value). They are called flags.

They can be:

writable - in the case it’s true, the value can be modified. Otherwise, it’s considered read-only.

enumerable -when it’s true, then it’s listed inside the loop. Otherwise- not listed.

configurable-in case it’s true, then the property may be deleted, and the attributes may be changed. Otherwise, not.

Now, let’s check out the ways of getting the flags.

There is a method known as Object.getOwnPropertyDescriptor, which allows querying the full information about a property.

The syntax of the Object.getOwnPropertyDescriptor method is the following:

let descriptor = Object.getOwnPropertyDescriptor(obj, propName);

The returned value will be the so-called “property-descriptor” object, which encompasses all the flags.

Here is an example:

let book = {
  name: "Javascript"
}; 
let descriptor = Object.getOwnPropertyDescriptor(book, 'name');
console.log(JSON.stringify(descriptor, null, 2));
/* property descriptor:
{
  "value": "Javascript",
  "writable": true,
  "enumerable": true,
  "configurable": true
}
*/

The Object.defineProperty can be used for changing the flags. Its syntax is demonstrated below:

Object.defineProperty(obj, propName, descriptor);

In case the property exists, the defineProperty will update its flags. In another way, it will generate a property with a particular value and flags, and if a flag is not provided, it will be assumed as false.

A property name, created with false flags, will look like this:

let book = {};
Object.defineProperty(book, "name", {
  value: "Javascript"
});
let descriptor = Object.getOwnPropertyDescriptor(book, 'name');
console.log(JSON.stringify(descriptor, null, 2));
/*
{
  "value": "Javascript",
  "writable": false,
  "enumerable": false,
  "configurable": false
}
 */

So, after a comparison with a correctly created book.name, shown in the examples above, all flags are false in this case. If it’s not what you want, it is recommended to set them true in the descriptor.

Further, we will demonstrate the effects of the flags.

Using Non-Writable Flag

For transforming the book.name into a non-writable one, it’s necessary to change the writable flag like this:

let book = {
  name: "Javascript"
};
Object.defineProperty(book, "name", {
  writable: false
});
book.name = "HTML"; // Error: Cannot assign to read only property 'name'
console.log(book.name);

After calling the code above, no one will be able to change the name of your user. It will be possible only after using their defineProperty and overriding yours.

The Usage of Non-enumerable Flag

In this paragraph, we will show you how to insert a custom toString to the user. Commonly, a built-in toString is considered non-enumerable for objects. And, it doesn’t occur inside for..in.

After adding your own toString, then it will occur in for..in as follows:

let book = {
  name: "Javascript",
  toString() {
    return this.name;
  }
};
// By default, our two properties are listed.
for (let key in book) {
  console.log(key); // name, toString
}

Also, you can set enumerable:false, then it won’t occur inside a for..in loop. Here is how you can do it:

let book = {
  name: "Javascript",
  toString() {
    return this.name;
  }
};
Object.defineProperty(book, "toString", {
  enumerable: false
});
// toString disappears:
for (let key in book) {
  console.log(key); // name  
}

Also, non-enumerable objects are eliminated from Object.keys this way:

let book = {
  name: "Javascript",
  toString() {
    return this.name;
  }
};
Object.defineProperty(book, "toString", {
  enumerable: false
});
// toString disappears:
console.log(Object.keys(book)); // name

The Usage of Non-Configurable Flag

A non-configurable flag or configurable:false exists for built-in objects and properties, too. Note, that it is not possible to remove a non-configurable property.

Here is an example of Math.PI, which is non-configurable, non-enumerable, and non-writable at the same time:

let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');
console.log(JSON.stringify(descriptor, null, 2));
/*
{
  "value": 3.141592653589793,
  "writable": false,
  "enumerable": false,
  "configurable": false
}
*/

The value of Math.PI can’t be changed or overwritten:

Math.PI = 3; // Error
// delete Math.PI won't work either

Please note that transforming a property into non-configurable is a one-way road: it is impossible to get it back using defineProperty. There is another notable exception, as well: non-configurable doesn’t mean non-writable. Hence, a value of a property that is non-configurable but writable can be modified.

The main purpose of configurable: false is restraining from changes to property flags and its removal and not the changes to its value.

More About Object.defineProperties

The Object.defineProperties(obj, descriptors) method allows specifying multiple properties simultaneously.

The syntax of Object.defineProperties(obj, descriptors) is the following:

Object.defineProperties(obj, {

  property1: descriptor1,

  property2: descriptor2
  // ...
});

To be more precise, let’s consider an example:

Object.defineProperties(book, {
  book1: {
    value: "Javascript",
    writable: false
  },
  book2: {
    value: "Html",
    writable: false
  },
  // ...
});

As you can see, multiple properties are set at once.

Observing Object.getOwnPropertyDescriptors

This method can be used for receiving all the property descriptors at the same time.

It can be used as a flag-aware means to clone an object along with Object.defineProperties like this:

let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));

As a rule, while cloning an object, an assignment is used for copying properties:

for (let key in book) {
  clone[key] = book[key]
}

Note that it can’t clone flags. To have a better result, it is recommended to use Object.defineProperties.




Do you find this helpful?

Related articles