DEV Community

Cover image for JavaScript Function Flags and Descriptors
Bello Osagie
Bello Osagie

Posted on • Edited on • Originally published at techstack.hashnode.dev

JavaScript Function Flags and Descriptors

We know that objects contain properties that are key-value pairs, but there's more to that — it includes the flag attributes.


Property flags

The 3 other attributes besides the value property are:

  • writable — override (change) current value, if true.
  • enumerable — properties are listed in a loop if true.
  • configurable — deleted property can have a configurable attribute if true.

We have been able to change, loop, and delete properties in an object because the 3 attributes listed above are true by default.

We can make the above attributes false to prevent changes, looping, and deletion of properties.

First, there are some methods we need to get or use.

It is recommended to use use strict at the top of your program or function when using any of the methods below:

  • Object.defineProperty: It returns a mutable object by modifying the existing property object. It doesn't affect the original object or property. That is it updates or changes the property flag.

Syntax:

Object.defineProperty(obj, key, descriptor)
Enter fullscreen mode Exit fullscreen mode
  • Object.defineProperties: We can also define many properties at once with the method Object.defineProperties.

Syntax:

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

See the example below:

Object.defineProperties(person, {
  surname: { value: "Bello", writable: false },
  firstName: { value: "Osagie", writable: false },
  // ...
});
Enter fullscreen mode Exit fullscreen mode
  • Object.getOwnPropertyDescriptor: returns a mutable object to describe a specific property (key). It doesn't affect the original object or property.

Syntax:

descriptor = Object.getOwnPropertyDescriptor(obj, key)
Enter fullscreen mode Exit fullscreen mode
  • Object.getOwnPropertyDescriptor: We can also get all property descriptors at once.

Syntax:

Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
Enter fullscreen mode Exit fullscreen mode

For a better cloning use Object.defineProperties

The example below does not copy flags even though we clone the object, person

...    ...    ...
for (let key in person) {
  clone[key] = person[key]
}
...    ...    ...
Enter fullscreen mode Exit fullscreen mode

for..in ignores symbolic properties, but Object.getOwnPropertyDescriptors doesn't, it returns all property descriptors including symbolic ones.

  • JSON.stringify: It converts an object to a JSON string. It optionally contains the replacer argument to replace values and an optional space argument to specify the amount of padding from the left. The spacer can be any character besides a number.

Syntax:

JSON.stringify(obj, [replacer[, space]] )
Enter fullscreen mode Exit fullscreen mode

For the case of this topic, the syntax above can be rewritten as shown below:

JSON.stringify(descriptor, [replacer[, space]] )
Enter fullscreen mode Exit fullscreen mode

See the example below:

const person = {
  name: "Bello"
};

const descriptor = Object.getOwnPropertyDescriptor(person, 'name');

console.log( JSON.stringify(descriptor, null, '- ' ) );
/*
{
- "value": "Bello",
- "writable": true,
- "enumerable": true,
- "configurable": true
}
*/
Enter fullscreen mode Exit fullscreen mode

Let's change the property of the existing property.

see the example below:

const person = {
  name: "Bello"
};

Object.defineProperty(person, "name", {
  value: "John"
});

const descriptor = Object.getOwnPropertyDescriptor(person, 'name');
console.log( JSON.stringify(descriptor, null, '- ' ) );

/*
{
- "value": "Bello",
- "writable": true,
- "enumerable": true,
- "configurable": true
}
*/

{
- "value": "John",
- "writable": true,
- "enumerable": true,
- "configurable": true
}
Enter fullscreen mode Exit fullscreen mode

The attributes are all true, but we can change any one of them or all to false (when an existing object is an empty object).

const person = {};

Object.defineProperty(person, "name", {
  value: "John"
});

const descriptor = Object.getOwnPropertyDescriptor(person, 'name');
console.log( JSON.stringify(descriptor, null, '- ' ) );

/*
{
- "value": "John",
- "writable": false,
- "enumerable": false,
- "configurable": false
}
*/
Enter fullscreen mode Exit fullscreen mode

Apart from getting the values of the attribute, we can also set the attribute values.

Let's specify these attributes below in the object.


Falsy flag properties

Non-writable

Let’s make person.name non-writable. See below:

'use strict'

const person = {
  name: "Bello"
};

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

person.name = "John"; // TypeError: Cannot assign to read only property 'name' of object '#<Object>'
Enter fullscreen mode Exit fullscreen mode

The existing person name can be changed only if the method defineProperty specifies the writable flag to be true.

See the example below:

'use strict'

const person = { };

Object.defineProperty(person, "name", {
  value: "John",
  writable: true
});

console.log(person.name); // John
person.name = "Bello"; // Bello
Enter fullscreen mode Exit fullscreen mode

Non-enumerable

Properties are listed in a loop if true, if false then they are skipped.

In the example below, the property, greet is listed in the for..in when enumerable is true.

const person = {
  name: "Bello",
  greet() {
    return `Hello ${this.name}.`;
  }
};

/*
Object.defineProperty(person, "greet", {
  enumerable: false
});
*/

for (let key in person) { 
  console.log(key);
  /*
  name
  greet
  */
}
Enter fullscreen mode Exit fullscreen mode

The above example is the default behavior. To avoid the greet, enumerable must be set to false.

See the example below:

const person = {
  name: "Bello",
  greet() {
    return `Hello ${this.name}.`;
  }
};

Object.defineProperty(person, "greet", {
  enumerable: false
});

for (let key in person) { 
  console.log(key);
  /*
  name
  */
}
Enter fullscreen mode Exit fullscreen mode

The claim above is also true for Object.keys. See below:

const person = {
  name: "Bello",
  greet() {
    return `Hello ${this.name}.`;
  }
};

Object.defineProperty(person, "greet", {
  enumerable: false
});

console.log(Object.keys(person));

/*
  name
*/
Enter fullscreen mode Exit fullscreen mode

Non-configurable

The configurable flag set to false is sometimes used on built-in objects and properties.

By default, we can change constant values when the configurable flag is true.

See the example below:

let x = Math.PI; // const x = Math.PI
x = 3;
console.log(x); //
Enter fullscreen mode Exit fullscreen mode

Note: const should be used on constants like Math.PI above, birthday, etc. For practice, we will use let.

A non-configurable property can not be deleted.

See the example below:

'use strict'

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

Math.PI = 3; 
// TypeError: Cannot assign to read only property 'PI' of object
Enter fullscreen mode Exit fullscreen mode

Also, you can't delete Math.PI when configurable is false.

see the example below:

'use strict'

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

delete Math.PI; // TypeError: Cannot delete property 'PI' of #<Object>
Enter fullscreen mode Exit fullscreen mode

See another example.

'use strict'

const Personbirth= {
  birthday: "1993 Nov, 3"
};

Object.defineProperty(Personbirth, "birthday", {
  configurable: false
});

Personbirth.birthday = "1993 Nov, 4";
delete Personbirth.birthday;
Enter fullscreen mode Exit fullscreen mode

A property that is become non-configurable cannot be changed back with defineProperty.

Other methods besides the methods mentioned earlier are listed below:

  • Object.preventExtensions(obj): Prevents adding of properties to an object. mplication implies writable: false.

  • Object.seal(obj): Prevents adding or removing of properties to an object. Implication implies configurable: false.

  • Object.freeze(obj): Prevents adding, removing, or changing of properties to an object. Implication implies configurable: false, writable: false.

  • Object.isExtensible(obj): Returns false if all current properties are non-writable writable: false, true otherwise.

  • Object.isSealed(obj): Returns true if all current properties are non-configurable configurable: false, false otherwise.

  • Object.isFrozen(obj): Returns true if all current properties are non-configurable and non-writable configurable: false, writable: false, false otherwise.


Buy me a Coffee


Top comments (0)