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)
- 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:
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, butObject.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:
JSON.stringify(descriptor, [replacer[, space]] )
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
}
*/
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
}
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
}
*/
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>'
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
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
*/
}
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
*/
}
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
*/
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); //
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
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>
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;
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-writablewritable: false
,true
otherwise.Object.isSealed(obj): Returns
true
if all current properties are non-configurableconfigurable: false
,false
otherwise.Object.isFrozen(obj): Returns
true
if all current properties are non-configurable and non-writableconfigurable: false, writable: false
,false
otherwise.
Top comments (0)