JavaScript Object Property Flags and Descriptors
— Modifying objects with descriptors and writable, enumerable and configurable flags.
In this blog we are going to look into manipulating Object and its properties in JavaScript, referencing its writable, enumerable and configurable flags.
But first, let’s revisit the Object
concept.
The Object
class represents one of JavaScript's data types. It is used to store various keyed collections and more complex entities. Objects can be created using the Object()
constructor (new Object()
, Object.create()
) or the object initialiser / literal syntax.
Normally, there are 4 types of property/method to think about:
- Those defined inside a constructor function that are given to object instances. Like the
name
property below.
class Person {
constructor(name) {
this.name = name;
}this.age = age
}Person.prototype.greeting = `Hi! I'm ${this.name}`
- Those defined directly on the constructor themselves, that are available only on the constructor, known as static properties/methods. Like the
age
above. - Those defined on a constructor’s prototype, which are inherited by all instances and inheriting object classes. Any member defined on a Constructor’s
prototype
property. Like thegreeting
method above. - Those available on an object instance. For example
me = new Person(); me.career = "teacher";
.
All the properties above are defined as “key-value” pairs. But there are some hidden attributes on them that are default if not set but have great power underneath.
Object properties, besides a value
, have flags.
Enumerable
Enumerable flag defines if the property is enumerable and show up in for…in loops unless the property’s key is a Symbol. If it’s set to false
then it also won’t be picked by Object.assign()
or spread operator.
var o = {};Object.defineProperty(o, 'a', {
value: 1,
enumerable: true
});Object.defineProperty(o, 'b', {
value: 2,
enumerable: false
});
for (var i in o) {
console.log(i);
} //'a'Object.keys(o); // ['a']
Writable
When the writable
property attribute is set to false
, the property is said to be “non-writable”. It cannot be reassigned.
'use strict'
var o = {}; Object.defineProperty(o, 'a', {
value: 37,
writable: false
});console.log(o.a); // 37
o.a = 25; // throws a TypeError
Configurable
The configurable
attribute controls at the same time whether the property can be deleted and whether its attributes (other than value
and writable
) can be changed.
- Can’t delete the property
- Can’t change
configurable
. - Can’t change
enumerable
. - Can’t change
writable: false
totrue
(works the other way around). - Can’t change
getter/setter
(but can assign them if absent).
var o = {};Object.defineProperty(o, 'a', {
get() { return 1; },
configurable: false
});
Object.defineProperty(o, 'a', {
set() {}
}); // throws a TypeError (set was undefined previously)Object.defineProperty(o, 'a', {
get() { return 1; }
}); // throws a TypeError
// (even though the new get does exactly the same thing)Object.defineProperty(o, 'a', {
value: 12
}); // throws a TypeError // ('value' can be changed when 'configurable' is false but not in this case due to 'get' accessor)
To define the object property in a formal way (instead of using assignment operator) we can use the below method.
Object.defineProperty(obj, prop, descriptor)
The static method Object.defineProperty()
defines a new property directly on an object, or modifies an existing property on an object, and returns the object.
const object1 = {};Object.defineProperty(object1, ‘property1’, {
value: 42,
writable: false
});
You can see the 3rd argument is descriptor. This is where all the 3 flags we mentioned earlier come into play. Property descriptors present in objects come in two types: data descriptors and accessor descriptors. A descriptor must be one of these two, it cannot be both.
- By default, both types have
configurable: false
andenumerable: false
.
But there are something specific to the kind.
- A data descriptor is a property that has a
value
field, defaults toundefined
, and awritable
field defaults tofalse
that indicates if the property can be reassigned. - An accessor descriptor is a property described by a getter-setter pair of functions.
get
is a function which serves as a getter for the property defaults toundefined
.set
is a function which serves as a setter for the property, defaults toundefined
.
Note that the flags have different values with different ways of adding properties and default values.
var o = {};o.a = 1;
// is equivalent to:
Object.defineProperty(o, 'a', {
value: 1,
writable: true,
configurable: true,
enumerable: true
});Object.defineProperty(o, 'a', { value: 1 });
// is equivalent to:
Object.defineProperty(o, 'a', {
value: 1,
writable: false,
configurable: false,
enumerable: false
});
That’s so much of it today!
Next blog we will look at the different methods to manipulate Objects.
Happy Reading!