Javascript Property Descriptors allow us to configure a JavaScript Property using the flags like enumerable, writable & configurable. We can read the Property Descriptors using the getOwnPropertyDescriptor and alter them using the DefineProperty.
Property Descriptors hold the configurations of an object’s property.
We can create a JavaScript object in many ways. Every property that we add to the object automatically gets its own Property descriptor. A property descriptor is a simple object and contains the metadata or information about the Property.
There are two types of Properties in JavaScript. Data Properties & Accessor Properties.
Data Properties maps to a value (i.e. primitive value, object, or function). Accessor Properties maps to getter & Setter methods. Each of these Property types also has its own Property Descriptors. Hence are two types of Property Descriptors.
A JavaScript Property can have only one of the Descriptors. It cannot have both Descriptors.
Data Descriptors is for Data Properties. It contains the following fields
When we create a new object using the Object literal Syntax or any other method, JavaScript adds the descriptor for each Property. By default, it creates the property Writable, Enumerable & configurable set to true. The Value property is set to the value of the Property.
Accessor Descriptors is for Accessor Properties. It contains the following fields
When we create a getter & setter property using the Object literal Syntax JavaScript adds this descriptor to the property. By default, it creates the descriptor with Enumerable & configurable set to true. The get & set mapped to the getter & setter functions.
const person = {
_name: "",
get name() {
return this._name;
},
set name(value) {
this._name = value;
},
};
You can read the Property Descriptor of any property using the getOwnPropertyDescriptor method.
Syntax
Object.getOwnPropertyDescriptor(obj, prop)
Where
obj is the object in which to look for the property..
prop name of the property whose description we want to retrieve
The following is a person object with name (accessor property) & age (data property) properties. We use the getOwnPropertyDescriptor to retrieve their descriptors
const person = {
_name: "",
get name() {
return this._name;
},
set name(value) {
this._name = value;
},
age:30
};
console.log(Object.getOwnPropertyDescriptor(person,'name'))
console.log(Object.getOwnPropertyDescriptor(person,'age'))
You can see from the output that the accessor property descriptor does not have writable & value property. The data property descriptor does not have get & set property
Also, note that configurable, enumerable & writable are set to true
We can modify the object returned by the getOwnPropertyDescriptor , but it will not change the descriptor of the original property
To change the descriptor of a Property, we can use the defineProperty or defineProperties method
The defineProperty adds a new property or modifies an existing property. It returns the object.
Syntax
To change the descriptor of a Property, we can use the defineProperty or defineProperties method
Object.defineProperty(obj, prop, descriptor)
Where
obj in which to look for the property.
propis the name of the property whose description we want to add or modify.
descriptor for the property that wants to add or modify
For a new property, If we do not provide any values for Writable, Enumerable & configurable, then they are set to false. The value is set to undefined.
Example
The following code creates an empty person object. We then add the _name & age data properties & name getter & setter method using the defineProperty method.
const person = {};
Object.defineProperty(person, "_name", {
value: "",
writable: true,
configurable: true,
enumerable: true,
});
Object.defineProperty(person, "name", {
get() {
return this._name;
},
set(value) {
this._name = value;
},
configurable: true,
enumerable: true,
});
Object.defineProperty(person, "age", {
value: "30",
writable: true,
configurable: true,
enumerable: true,
});
But if the property already exists, the existing values are overwritten only if the values are provided.
const person = {
age:10
};
console.log(Object.getOwnPropertyDescriptor(person,'age'))
//{value: 10, writable: true, enumerable: true, configurable: true}
//Does not affect as the descriptoris empty
Object.defineProperty(person, "age", {});
console.log(Object.getOwnPropertyDescriptor(person,'age'))
//{value: 10, writable: true, enumerable: true, configurable: true}
It is similar to defineProperty but allows us to add or modify multiple properties
Syntax
Object.defineProperties(obj, props)
Where
Syntax
It is similar to defineProperty but allows us to add or modify multiple properties
obj in which to look for the property.
props is an object whose each property must be a descriptor (either a data descriptor or an accessor descriptor). The name of the key becomes the property name.
const person = {};
obj= {
_name : { value: "", writable: true, configurable: true, enumerable: true, },
name : { get() { return this._name; },
set(value) { this._name = value; },
configurable: true, enumerable: true, },
age : { value: "30", writable: true, configurable: true, enumerable: true, }
}
Object.defineProperties(person,obj);
console.log(person)
This Property of the Property Descriptor holds the Value of the Property.
const person = {};
//Creating new property. note that writable is set true.
Object.defineProperty(person, "age", { value:10, writable:true});
console.log(person.age) //10
//No values is provided. does not overwrite
Object.defineProperty(person, "age", {});
console.log(person.age) //10
//Sets the age as 30
Object.defineProperty(person, "age", { value:30});
console.log(person.age) //30
person.age=40
console.log(person.age) //40
Writable determines whether you can modify the value of the property or not. A false value makes property read-only.
The following code creates the age property with the value of 10 with writable as false.
Assigning a new value of 20 will fail. If you have use strict enabled, then JavaScript throws an error else it will fail silently.
"use strict"
const person = {};
//Creating new property.
Object.defineProperty(person, "age", { value:10,writable:false, configurable:true,enumerable:true});
console.log(person.age) //10
//
person.age=20
//
//Cannot assign to read only property 'age' of object '#<Object>'//
//Remove use strict
//Error is thrown only when using use strict. else fails silently
console.log(person.age) //10
In this example, we revert back the writable:false to writable:true. Now you can modify the property.
const person = {};
//Creating new property.
Object.defineProperty(person, "age", { value:10,writable:false, configurable:true,enumerable:true});
console.log(person.age) //10
person.age=20 //fails
console.log(person.age) //10
//Makng Writable true again
Object.defineProperty(person, "age", { writable:true});
person.age=20 //works now
console.log(person.age) //20
An enumerable flag determines if we can view the property using the for..in loop or Object.keys() method.
The person object in the following example has three properties. Both for..in loop & Object.keys() returns all three because enumerable by default set to true
const person = {
prop1:"one",
prop2:"two",
prop3:"three"
};
console.log(Object.keys(person)) //["prop1", "prop2", "prop3"]
for (const key in person) {
console.log(key)
}
//prop1
//prop2
//prop3
Object.defineProperty(person, "prop2", { enumerable:false});
console.log(Object.keys(person)) //["prop1", "prop3"]
for (const key in person) {
console.log(key)
}
//prop1
//prop3
We modify the prop2 and set its enumerable to false. Now both for..in loop & Object.keys() will not return prop2
Configurable property if true allows us to modify the writable & enumerable property of an object. It also allows us to delete the property. But if it is set to false, then you cannot change the value of enumerable neither you can delete the property. It allows changing the writable from true to false but not from false to true. Once you set configurable as false, you cannot set it true again.
The following code makes the firstName non-configurable. Now you cannot modify its enumerable flag.
"use strict"
const person = {
firstName:"Bill",
lastName:"Gates",
};
Object.defineProperty(person, "firstName", { configurable:false});
Object.defineProperty(person, "firstName", { enumerable:false});
//Uncaught TypeError: Cannot redefine property: firstName
But you can change writable from true to false. But not from false to true.
"use strict"
const person = {
firstName:"Bill",
lastName:"Gates",
};
Object.defineProperty(person, "firstName", { configurable:false});
//No Error here. You can change true to false
Object.defineProperty(person, "firstName", { writable:false});
//But cannot make it writable again
Object.defineProperty(person, "firstName", { writable:true});
//Uncaught TypeError: Cannot redefine property: firstName
Non-configurable properties cannot be deleted.
"use strict"
const person = {
firstName:"Bill",
lastName:"Gates",
};
Object.defineProperty(person, "firstName", { configurable:false});
delete person.lastName //deleted
console.log(person)
//cannot delete
delete person.firstName
//Uncaught TypeError: Cannot delete property 'firstName' of #<Object>
Setting configurable to false is a one-way street. You cannot set it to true again
"use strict"
const person = {
firstName:"Bill",
lastName:"Gates",
};
Object.defineProperty(person, "firstName", { configurable:false});
Object.defineProperty(person, "firstName", { configurable:true});
//Uncaught TypeError: Cannot redefine property: firstName
The Get & Set property descriptors are mapped to the Getter and Setter functions. The getter & setter methods are known as accessor properties in JavaScript. They look like normal properties but are actually functions mapped to a Property
const person = {};
obj= {
_name : { value: "", writable: true, configurable: true, enumerable: true, },
name : { get() { return this._name; },
set(value) { this._name = value; },
configurable: true, enumerable: true, },
}
Read More