Advanced Javascript Properties
It is possible to configure object properties in Javascript for example to set properties to be pseudo-private or readonly. This feature is available since ECMAScript 5.1, therefore supported by all recent browsers.
To do so, you need to use the method defineProperty
of the Object
prototype like so:
var a = {};
Object.defineProperty(a, 'readonly', {
value: 15,
writable: false
});
a.readonly = 20;
console.log(a.readonly); // 15
The syntax is as follows:
Object.defineProperty(dest, propName, options)
or for multiple definitions:
Object.defineProperties(dest, {
propA: optionsA,
propB: optionsB, //...
})
where options include the following attributes:
- value: if the property is not a getter (see below), value is a mandatory attribute.
{a: 12}
===Object.defineProperty(obj, 'a', {value: 12})
- writable: set the property as readonly. Note that if the property is a nested objects, its properties are still editable.
- enumerable: set the property as hidden. That means that
for ... of
loops andstringify
will not include the property in their result, but the property is still there. Note: That doesn’t mean that the property is private! It can still be accessible from the outside, it just means that it won’t be printed. - configurable: set the property as non modifiable, e.g. protected from deletion or redefinition. Again, if the property is a nested object, its properties are still configurable.
So in order to create a private constant property, you can define it like so:
Object.defineProperty(obj, 'myPrivateProp', {value: val, enumerable: false, writable: false, configurable: false});
Besides configuring properties, defineProperty
allows us to define dynamic properties, thanks to the second parameter being a string. For instance, let’s say that I want to create properties according to some external configuration:
var obj = {
getTypeFromExternal(): true // illegal in ES5.1
}
Object.defineProperty(obj, getTypeFromExternal(), {value: true}); // ok
// For the example sake, ES6 introduced a new syntax:
var obj = {
[getTypeFromExternal()]: true
}
But that’s not all! Advanced properties allows us to create getters and setters, just like other OOP languages! In that case, one cannot use the writable
, enumerable
and configurable
properties, but instead:
function Foobar () {
var _foo; // true private property
Object.defineProperty(obj, 'foo', {
get: function () { return _foo; }
set: function (value) { _foo = value }
});
}
var foobar = new Foobar();
foobar.foo; // 15
foobar.foo = 20; // _foo = 20
Aside for the obvious advantage of encapsulation and advanced accessors, you will notice that we didn’t “call” the getter, instead we just “get” the property without parentheses! This is awesome! For instance, let’s imagine that we have an object with long nested properties, like so:
var obj = {a: {b: {c: [{d: 10}, {d: 20}] } } };
Now instead of doing a.b.c[0].d
(where one of the properties can resolve to undefined
and throw an error), we can instead create an alias:
Object.defineProperty(obj, 'firstD', {
get: function () { return a && a.b && a.b.c && a.b.c[0] && a.b.c[0].d }
})
console.log(obj.firstD) // 10
Note
If you define a getter without a setter and still try to set a value, you will get an error! This is particularly important when using helper functions such as $.extend
or _.merge
. Be careful!
Links
Use the 100 answers in this short book to boost your confidence and skills to ace the interviews at your favorite companies like Twitter, Google and Netflix.
GET THE BOOK NOWA short book with 100 answers designed to boost your knowledge and help you ace the technical interview within a few days.
GET THE BOOK NOW