Flags and Descriptors

Pradipta Choudhury
Last Updated: May 13, 2022

Introduction

As we already know, objects can store properties.

Till now, we know that property is nothing but a “key-value” pair to us. But, an object’s property is much more stronger and powerful.

In this article, we will learn some additional configuration options and in the next articles, we will also learn how to invisibly turn them using getter/setter options.

 

Property flags

There are three special attributes(also called “flags”):

  • writable: if this condition satisfies, then only values can be changed. Otherwise, it’s only read-only.
  • enumerable: if this condition is true, then only values are listed. Otherwise not listed.
  • configurable: if this condition is true, then the property can be deleted and modified, otherwise not.

 

Whenever we create a property, just the way we used to do, all the above flags are set to true by default. But we can change them anytime. First, we need to see how can we get those flags, come let’s find that out.

 

happy youtube GIF by Rosanna Pansino

 Source: Giphy

 

The full information about the property is queried by the method ObjectgetOwnPropertyDescriptor. The syntax is:

 

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

 

Here,

  •  the obj is the object to get information from.
  • propertyName is the name of the property.
  • The returned value is known as the “property-descriptor” object. It contains the required value and all the flags.

 

let user = {
  name: "James"
};

let descriptor = Object.getOwnPropertyDescriptor(user, 'name');

alert( JSON.stringify(descriptor, null2 ) );
/* Property descriptor contains following information:
{
  "value": "James",
  "writable": true,
  "enumerable": true,
  "configurable": true
}
*/

 

Output:

 

For changing the flags, Object.defineProperty can be used. The syntax for the same is:

Object.defineProperty(obj, propertyName, descriptor)

 

Here, obj is the object and propertyName is the name of the property. The descriptor is the property descriptor object.  

If a property is present, defineProperty will be updating the flags. Otherwise, the property is created with provided value and flags. In other words, if a flag is not supplied, then it is assumed to be false.

 

let user = {};

Object.defineProperty(user, "name", {
  value: "James"
});

let descriptor = Object.getOwnPropertyDescriptor(user, 'name');

alert( JSON.stringify(descriptor, null2 ) );
/*
{
  "value": "James",
  "writable": false,
  "enumerable": false,
  "configurable": false
}
*/

 

Output:

 

Compare both the codes provided above, one with normally created and another with object declaration. We will find that all the flags are false. If we want to set their values, then change them to true.

 

Non-writable

By changing the writable flag, the user.name can be made non-writable.

 

<!DOCTYPE html>
<script>
"use strict";

let user = {
  name: "James"
};

Object.defineProperty(user, "name", {
  writable: false
});

user.name = "Peter"// Error: Cannot assign to read only property 'name'
</script>

 

On running the above code, errors will come. Errors will only appear in the strict mode. In the non-strict mode, when writing to non-writable properties, no errors will be occurring. Another consequence is that many flag violating actions will simply get violated.

 

<!DOCTYPE html>
<script>
"use strict";

let user = { };

Object.defineProperty(user, "name", {
  value: "James",
  // new properties need to be explicitely listed
  enumerable: true,
  configurable: true
});

alert(user.name); // James
user.name = "Peter"// Error
</script>

 

Output:

Non-enumerable

Now, let’s see how we can add custom toString to the user. The built-in function for the strings is generally enumerable. But if we create a toString on our own, then by default it shows up like this:

 

<!DOCTYPE html>
<script>
"use strict";

let user = {
  name: "James",
  toString() {
    return this.name;
  }
};

// By default, both our properties are listed:
for (let key in user) alert(key); // name, toString
</script>

 

Output:

 

 

If you want to change it, it can be changed by writing enumerable:false. This will change the default value.

 

<!DOCTYPE html>
<script>
"use strict";

let user = {
  name: "James",
  toString() {
    return this.name;
  }
};

Object.defineProperty(user, "toString", {
  enumerable: false
});

// Now our toString disappears:
for (let key in user) alert(key); // name
</script>

 

Output:

 

Object.keys don’t include the enumerable properties.

 

Non-configurable

The presence of a non-configurable flag is very less for built-in objects and properties. Non-configurable properties can’t be modified or deleted. Math.PI is non-enumerable, non-writable, and non-configurable.

 

<!DOCTYPE html>
<script>
"use strict";

let descriptor = Object.getOwnPropertyDescriptor(Math'PI');

alert( JSON.stringify(descriptor, null2 ) );
/*
{
  "value": 3.141592653589793,
  "writable": false,
  "enumerable": false,
  "configurable": false
}
*/
</script>

 

Output:

 

Here, the programmer will not be able to change the value of Math.PI or overwrite it. Hence, we can conclude that if any property is made non-configurable nothing can be done to it. It’s just like a one-way road. It cannot be changed back with defineProperty

 

If we prefer using configurable: false, the values can be changed to some extent. Configurable: false prevents changes of property flags and deletion.

 

<!DOCTYPE html>
<script>
"use strict";

let user = {
  name: "James"
};

Object.defineProperty(user, "name", {
  configurable: false
});

user.name = "Peter"// This works fine
delete user.name; // Error is coming
</script>

 

Again, let’s make the user.name a forever sealed constant. This will also work similarly to Math. PI.

 

<!DOCTYPE html>
<script>
"use strict";

let user = {
  name: "James"
};

Object.defineProperty(user, "name", {
  writable: false,
  configurable: false
});

// cannot change user.name or its flags
user.name = "Peter";
delete user.name;
Object.defineProperty(user, "name", { value: "Peter" });
</script>

 

object.defineProperties

Many properties can be defined at once using a method called Object.defineProperties(obj, descriptors). The syntax for the same is:

 

Object.defineProperties(obj, {
  prop1: descriptor1,
  prop2: descriptor2
  // ...
});

 

To be specific:

Object.defineProperties(user, {
  name: { value: "James", writable: false },
  surname: { value: "Mille", writable: false },
  // ...
});

 

Object.getOwnPropertyDescriptors

For getting all property descriptors at once, the method Object.getOwnPropertyDescriptors(obj) can be used.

If used together with object.defineProperties, it can be used as “flags-aware” way of cloning an object. Syntax for the same is:

 

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

Normally, for cloning any object, an assignment is used for copying the properties:

 

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

 

But, the flags cannot be copied with this. For better cloning, Object.defineProperties is used. 

 

Sealing an object globally

At the level of individual properties, property descriptors play their roles. Access to the whole object can be restricted by some methods. Let’s look at some of them:

 

  •  Object.preventExtensions(obj) : this method restricts the addition of new properties to the object.

 

  • Object.freeze(obj): this method restricts the addition or removal or modification of properties. This also resets the configurable: false and writable: false for all the existing properties.

 

  • Object.seal(obj): this method restricts the addition or removal of properties. This resets the value configurable: false for all the existing properties.

 

  • Object.isExtensible(obj): this method returns false, if addition of properties is restricted. Otherwise, returns true.

 

  • Object.isSealed(obj): this method returns true if removal or addition of new properties is forbidden and all existing properties have default value configurable: false.

 

  • Object.isFrozen(obj): this method returns true if the addition or modification or changing of new properties is restricted and the default value set is configurable: false and writable: false.

 

FAQs

  1. Mention the significance of object define property?
    defineProperty() helps to define new properties for an object, and if already present modifies the existing one and returns the object.
     
  2. Define property descriptors in Javascript?
    It is a simple Javascript object that is present with every object and contains information about its properties like object and its meta-data.
     
  3. What are the types of object properties in Javascript?
    There are two types of properties in Javascript: data properties and accessor properties.

 

Key takeaways:

In this article, we are introduced with property flags, non-writable, non-enumerable, non-configurable, and some of the important methods like object.defineProperties, and many more. We also covered some of the frequently asked questions which you might have come to your mind while going through this article.

Keeping the theoretical knowledge at our fingertips helps us get about half the work done. To gain complete understanding, practice is a must. To achieve thorough knowledge, visit our page.

 

Was this article helpful ?
0 upvotes