JavaScript Function Flags and Descriptors
Tags: javascript, webdev, codenewbie, tutorial
Published: 2021-04-25 20:00:03

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

---

### Property flags
The 3 other attributes besides the value property are:
- writable &mdash; override (change) current value, if `true`.
- enumerable &mdash; properties are listed in a loop if `true`. 
- configurable  &mdash; 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)
```

- Object.defineProperties: We can also define many properties at once with the method ` Object.defineProperties`.

Syntax:

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

See the example below:

```js
Object.defineProperties(person, {
  surname: { value: "Bello", writable: false },
  firstName: { value: "Osagie", writable: false },
  // ...
});
```

- `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)
```

- `Object.getOwnPropertyDescriptor`: We can also get all property descriptors at once.

Syntax:

```
Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
```

> 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]
}
...    ...    ...
```

> `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]] )
```

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

```js
JSON.stringify(descriptor, [replacer[, space]] )
```

See the example below:

```js
const person = {
  name: "Bello"
};

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

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

Let's change the property of the existing property.

see the example below:

```js
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
}
```

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).

```js
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
}
*/
```

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:

```js
'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>'
```

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

See the example below:

```js
'use strict'

const person = { };

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

console.log(person.name); // John
person.name = "Bello"; // Bello
```

---

#### 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`.

```js
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
  */
}
```

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

See the example below:

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

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

for (let key in person) { 
  console.log(key);
  /*
  name
  */
}
```

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

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

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

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

/*
  name
*/
```

---

#### 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:

```js
let x = Math.PI; // const x = Math.PI
x = 3;
console.log(x); //
```

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:

```js
'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
```

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

see the example below:

```js
'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>
```

See another example.

```js
'use strict'

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

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

Personbirth.birthday = "1993 Nov, 4";
delete Personbirth.birthday;
```

> 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)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions): Prevents adding of properties to an object. mplication implies `writable: false`.

- [Object.seal(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal): Prevents adding or removing of properties to an object. Implication implies `configurable: false`.

- [Object.freeze(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze): Prevents adding, removing, or  changing of properties to an object. Implication implies `configurable: false, writable: false`.

- [Object.isExtensible(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible): Returns `false` if all current properties are non-writable `writable: false`, `true` otherwise.

- [Object.isSealed(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed): Returns `true` if all current properties are non-configurable `configurable: false`, `false` otherwise.

- [Object.isFrozen(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen):  Returns `true` if all current properties are non-configurable and non-writable `configurable: false, writable: false`, `false` otherwise.

---
[![Buy me a Coffee](https://res.cloudinary.com/bizstak/image/upload/v1610317510/ko-fi-trans_fr3su4.png)](https://www.buymeacoffee.com/techstackmedia)

---