Property descriptor
It is known that Javascript is a language that does extensive use of objects.
To make some of the language features work, it is necessary to associate some information to each of the object properties. This is an example of metadata.
In that sense, a property descriptor (in English Property Descriptor) is a type of meta-object that is associated with any property of any Javascript object. It contains relevant information about how the Runtime of the language will treat the property to which it belongs.
Given the following object:
const obj = {
a: 1,
b: 2
};
Each property descriptor corresponds to a certain property of an object. Thus, in the example above, all properties of obj1
- how a
, b
and those inherited by the prototype - have an associated property descriptor.
The property descriptors contain the property attributes (Property Attributes), that control the property to which the property descriptor refers.
Property attributes
Each property of an object has attributes (so-called property attributes - or Property attribute). They control how the property will work. They are authentic property metadata.
By convention, property attributes are referred with a notation using two brackets. For example, [[Enumerable]]
.
There are six properties attributes, but they are grouped according to the function of the object property.
Specific attributes of data properties:
[[Value]]
, that contains the value of the property. Its default value is undefined
.
[[Writable]]
, a boolean that defines if the property value ([[Value]]
) can be changed. Its default value is false
.
Specific attributes of advisory properties (getters
or setters
):
[[Get]]
, containing the function getter
, invoked when the property is read. Its default value is undefined
.
[[Set]]
, containing the function setter
, invoked when the property is set or reset. Its default value is undefined
.
General attributes (both of data properties how much of advisory properties):
[[Enumerable]]
, a boolean that determines whether the property is enumerable. Its default value is false
.
[[Configurable]]
, a boolean that determines whether the property is configurable. Its default value is false
.
There are several methods that allow you to create or manipulate properties with their modified attributes. Some of them are:
Object.create
, which creates a new object. You can pass, in the second argument, an object containing all the initial properties with their respective property descriptors. Within each descriptor, as stated above, go the attributes.
Object.defineProperty
, defining or modifying a property and its descriptor.
For example, below we create a new object whose property name
is enumerable, and lastName
is not enumerable:
const obj = Object.create(null, {
name: {
value: 'Luiz',
enumerable: true,
writable: true,
configurable: true
},
lastName: {
value: 'Felipe',
enumerable: false,
writable: true,
configurable: true
},
});
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
Below contains a brief explanation and detail about each of the property attributes, as well as some observations.
[[Value]]
The attribute of ownership [[Value]]
contains the value of the data property. It does not determine behaviors beyond the value itself.
[[Writable]]
The attribute of ownership [[Writable]]
is a ticket determining whether ownership can be changed through the allocation operator (=
).
In the strictly speaking language, any attempt to change a value whose attribute [[Writable]]
be as false
make an exception. The method Reflect.set
can be used to try to change a property without throwing exceptions, even if [[Writable]]
for false
.
[[Get]]
The attribute of ownership [[Get]]
determines a function that will be called, without any argument, whenever the property is read.
For example, assuming that name
is an advisory property, its attribute [[Get]]
will be invoked when:
// Irá invocar o atributo `[[Get]]` da propriedade `name` do objeto `obj`.
obj.name;
When the notation get
is used, an advisory property is being created with its attribute [[Get]]
.
[[Set]]
The attribute of ownership [[Set]]
determines a function that will be called, with an argument, whenever the property is changed.
For example, assuming that name
is an advisory property, its attribute [[Set]]
will be invoked when:
// Irá invcar o atributo `[[Set]]` da propriedade `name` do objeto `obj`.
// Passará a string 'Luiz' como primeiro (e único) argumento.
obj.name = 'Luiz';
When the notation set
is used, an advisory property is being created with its attribute [[Set]]
.
General attributes (both of data properties how much of advisory properties):
[[Enumerable]]
The atribto of property [[Enumerable]]
is a boolean that determines whether the property can be enumerated or not.
The noose for..in
and functions as Object.keys
or JSON.stringify
will only work with properties whose attribute [[Enumerable]]
be it true
. Non enumerable properties will not be listed by these means.
However, you can use methods such as Object.getOwnPropertyNames
, Object.getOwnPropertySymbols
and Reflect.ownKeys
to list all properties of the object - enumerable or not. See:
const obj = Object.create(null, {
name: {
value: 'Luiz',
enumerable: true, // ⚠️
writable: true,
configurable: true
},
lastName: {
value: 'Felipe',
enumerable: false, // ⚠️
writable: true,
configurable: true
},
});
console.log(
Object.keys(obj) // ['name']
);
console.log(
Object.getOwnPropertyNames(obj) // ['name', 'lastName']
);
[[Configurable]]
The attribute of ownership [[Configurable]]
is a boolean that determines whether the property can be configured. This means that if a property has its attribute [[Configurable]]
defined as false
, you will not be able to:
- Delete object property (using
delete
or any other means, such as Reflect.deleteProperty
);
- Change your property descriptor - property attributes can no longer be edited for that property. That means you won’t even be able to change
[[Configurable]]
back to true
!
- You may, however, in the case of data properties, change the attribute
[[Value]]
, case [[Writable]]
is defined as true
.
- You may also change
[[Writable]]
for false
, but nay the other way around.
This last exception (to be able to make [[Writable]]
false
) already exists that, in Javascript, the property length
arrays always had the attribute [[Configurable]]
defined as false
, but [[Writable]]
, as true
. Thus, to implement the method Object.freeze
, Ecmascript specification needed to establish that last exception to not break legacy code.
Reference: