Creating Object Properties in JavaScript
There are a couple of ways to assign properties to objects in JavaScript. The most common example is using obj.field = value or obj[‘field’] = value. This approach is simple however, it is not flexible because it automatically defines property descriptor fields
let obj1 = {
foo: 'bar'
};
let obj2 = {
get foo() {
return 'bar';
}
};
let obj3 = Object.create(
{},
{ foo : { value : 'bar' } }
);
let obj4 = Object.create({}, {
foo : {
get : function() { return 'bar'; }
}
});
obj1.foo; // bar
obj2.foo; // bar
obj3.foo; // bar
obj4.foo; // bar
In all 4 obj objects, the foo property returns the same result. But are they the same? Obviously not. This post series examines these differences and shows how you can apply and leverage these capabilities.
Data and Accessor Property Descriptors
Property descriptors hold descriptive information about object properties. There are two types of property descriptors:
- Data descriptors – which only hold information about data
- Accessor descriptors – which hold information about accessor (get/set) descriptors.
A property descriptor is a data structure with a couple of identifying fields, some are shared between both types while the others apply to a single type as shown below.
Data descriptor | Accessor descriptor | |
---|---|---|
value | Yes | No |
writable | Yes | No |
enumerable | Yes | Yes |
configurable | Yes | Yes |
get | No | Yes |
set | No | Yes |
Viewing Property Descriptor information
The getOwnPropertyDescriptor allows you to get the property descriptor for any object.
let dataDescriptor =
Object.getOwnPropertyDescriptor(obj1, 'foo');
dataDescriptor;
// Object {
// value: "bar",
// writable: true,
// enumerable: true,
// configurable: true
// }
let accessorDescriptor =
Object.getOwnPropertyDescriptor(obj2, 'foo');
accessorDescriptor;
// Object {
// get: function foo () {}
// set: undefined,
// enumerable: true,
// configurable: true
// }
Data Descriptor only fields
- Value: Gets the value of the property.
- Writable: Boolean indicating whether the property value can be changed. This can be used to create ‘constant‘ field values especially for primitive values.
Accessor Descriptor only fields
- Get: Function which will be invoked whenever the property is to be retrieved. This is similar to getters in other languages.
- Set: Function that would be invoked when the property is to be set. It’s the setter function.
Shared fields
- Enumerable: Boolean indicating whether the property can be enumerated. This determines if the property shows up during enumeration. For example, with for..of loops or Object.keys.
- Configurable: Boolean indicating whether the type of the property can be changed and if the property can be deleted from the object.
Setting Property Descriptors
The Object.defineProperty method allows you to specify and define these property descriptor fields. It takes the object, property key and a bag of descriptor values.
let obj5 = {};
Object.defineProperty(obj5, 'foo', {
value: 'bar',
writable: true,
enumerable: true,
configurable: true
});
obj5.foo; // bar
let obj6 = {};
Object.defineProperty(obj6, 'foo', {
get: function() { return 'bar'; }
});
obj6.foo; // bar
Default values for Object Descriptors
All boolean descriptor fields default to false while the getter, setter and value properties default to undefined. This is an important detail that is most visible when creating and modifying properties via object asssignment or the defineProperty method.
let sample = { a : 2 };
Object.defineProperty(sample, 'b', { value: 4 });
sample; // { a: 2, b:4 }
Object.getOwnPropertyDescriptor(sample, 'a');
// Object {
// value: 2,
// writable: true,
// enumerable: true,
// configurable: true
// }
Object.getOwnPropertyDescriptor(sample, 'b');
// Object {
// value: 4,
// writable: false,
// enumerable: false,
// configurable: false
// }
sample.b = 'cannot change'; //writable = false
sample.b //4
delete sample.b //configurable=false
sample.b //4
Object.keys(sample); //enumerable = false
// ['a']
Because the other properties of property b were not set on creation, they default to false. This effectively makes b immutable, not configurable and not enumerable on sample.
Validating property existence on JavaScript Objects
Three tricky scenarios:
- Accessing non-existent property fields results in undefined
- Due to the default rules, accessing existing property fields with no value set also gives undefined
- Finally, it is possible to define a property with the value undefined
So how do you verify if a property actually exists and has the value undefined or if doesn’t exist at all on an object?
let obj = { a: undefined };
Object.defineProperty(obj, 'b', {}); //use defaults
obj.a; //undefined
obj.b; //undefined
obj.c; //undefined
HasOwnProperty to determine Property Existence
The way out of this is the hasOwnProperty function.
<pre class="wp-block-syntaxhighlighter-code">
Object.hasOwnProperty('a'); //true
Object.hasOwnProperty('b'); //true
Object.hasOwnProperty('c'); //false
</pre>
Conclusion
There is still a lot more about these values and how to use them. But that would make this post too long so this would be a series. In the next post, the theme would be about each field and what it can be used for.
Teasers before the next post
- Try invoking a getter property as a function to see what happens. Can you explain why?
- Try modifying some of the descriptor properties of native JavaScript objects e.g. RegExp, Array, Object etc. What happens?
Related
Read the second post in this series or check out other related articles:
Hello,
Over here:
Object.hasOwnProperty(‘a’); //true
Object.hasOwnProperty(‘b’); //true
Object.hasOwnProperty(‘c’); //false
I think you meant ‘obj’ instead of ‘Object’.
LikeLike