- Advertisement -

- Advertisement -

A minimal guide to ECMAScript Decorators

0 6

Get real time updates directly on you device, subscribe now.

- Advertisement -

To understand decorators, we need to first understand what is a property descriptor of JavaScript object property. A property descriptor is a set of rules on an object property, like whether a property is writable or enumerable. When we create a simple object and add some properties to it, each property has default property descriptor.

var myObj = {
myPropOne: 1,
myPropTwo: 2
};

myObj is a simple JavaScript object which looks like below in the console.

- Advertisement -

Now, if we write new value to myPropOne property like below, operation will be successful and we will get the changed value.

myObj.myPropOne = 10;
console.log( myObj.myPropOne ); //==> 10

To get property descriptor of property, we need to use Object.getOwnPropertyDescriptor(obj, propName) method. Own here means return property descriptor of propName property only if that property belongs to the object obj and not on it’s prototype chain.

let descriptor = Object.getOwnPropertyDescriptor(
myObj,
'myPropOne'
);
console.log( descriptor );

Object.getOwnPropertyDescriptor method returns an object with keys describing the permissions and current state of the property. value is the current value of the property, writable is whether user can assign new value to the property, enumerable is whether this property will show up in enumerations like for in loop or for of loop or Object.keys etc. configurable is whether user has permission to change property descriptor and make changes to writable and enumerable. Property descriptor also has get and set keys which are middleware functions to return value or update value, but these are optional.

To create new property on an object or update existing property with a custom descriptor, we use Object.defineProperty. Let’s modify an existing property myPropOne with writable set to false, which should disable writes to myObj.myPropOne.

'use strict';
var myObj = {
myPropOne: 1,
myPropTwo: 2
};
// modify property descriptor
Object.defineProperty( myObj, 'myPropOne', {
writable: false
} );
// print property descriptor
let descriptor = Object.getOwnPropertyDescriptor(
myObj, 'myPropOne'
);
console.log( descriptor );
// set new value
myObj.myPropOne = 2;

As you can see from above error, our property myPropOne is not writable, hence if a user is trying to assign new value to it, it will throw error.

If Object.defineProperty is updating existing property descriptor, then original descriptor will be overridden with new modifications. Object.defineProperty returns the original object myObj after changes.

Let’s see what will happen if we set enumerable descriptor key to false.

var myObj = {
myPropOne: 1,
myPropTwo: 2
};
// modify property descriptor
Object.defineProperty( myObj, 'myPropOne', {
enumerable: false
} );
// print property descriptor
let descriptor = Object.getOwnPropertyDescriptor(
myObj, 'myPropOne'
);
console.log( descriptor );
// print keys
console.log(
Object.keys( myObj )
);

As you can see from above result, we can’t see myPropOne property of the object in Object.keys enumeration.

When you define a new property on object using Object.defineProperty and pass empty {} descriptor, the default descriptor looks like below.

Now, let’s define a new property with custom descriptor where configurable descriptor key is set to false. We will keep writable to false and enumerable to true with value set to 3.

var myObj = {
myPropOne: 1,
myPropTwo: 2
};
// modify property descriptor
Object.defineProperty( myObj, 'myPropThree', {
value: 3,
writable: false,
configurable: false,
enumerable: true
} );
// print property descriptor
let descriptor = Object.getOwnPropertyDescriptor(
myObj, 'myPropThree'
);
console.log( descriptor );
// change property descriptor
Object.defineProperty( myObj, 'myPropThree', {
writable: true
} );

By setting configurable descriptor key to false, we lost ability to change descriptor of our property myPropThree. This is very helpful if you don’t want your users to manipulate recommended behaviour of an object.

get (getter) and set (setter) for a property can also be set in property descriptor. But when you define a getter, it comes with some sacrifices. You can not have a initial value or value key on the descriptor at all because getter will return the value of that property. You can not use writable key on descriptor as well, because your writes are done through the setter and you can prevent writes there. Have a look at MDN documentation of getter and setter, or read this article because they don’t need much explanation here.

You can create and/or update multiple properties at once using Object.defineProperties which takes two arguments. First argument is target object on which properties has to be added/modified and second argument is object with key as property name and value as it’s property descriptor. This function returns the target object.

Have you tried Object.create function to create objects? This is the easiest way to create an Object with no or custom prototype. It is also one of the easier way to create Object from scratch with custom property descriptors.

Object.create function has following syntax.

var obj = Object.create( prototype, { property: descriptor, ... } )

Here prototype is an object which will be prototype of the obj. If prototype is null, then obj won’t have any prototype. When you define an empty or non-empty object with var obj= {} syntax, by default, obj.__proto__ points to Object.prototype hence obj has prototype of Object class.

This is similar to using Object.create with Object.prototype as first argument (prototype of object being created).

'use strict';
var o = Object.create( Object.prototype, {
a: { value: 1, writable: false },
b: { value: 2, writable: true }
} );
console.log( o.__proto__ );
console.log(
'o.hasOwnProperty( "a" ) => ',
o.hasOwnProperty( "a" )
);

But when we set prototype to null, we get below error.

'use strict';
var o = Object.create( null, {
a: { value: 1, writable: false },
b: { value: 2, writable: true }
} );
console.log( o.__proto__ );
console.log(
'o.hasOwnProperty( "a" ) => ',
o.hasOwnProperty( "a" )
);

- Advertisement -

Get real time updates directly on you device, subscribe now.

- Advertisement -

- Advertisement -

Leave A Reply

Your email address will not be published.

x

We use cookies to give you the best online experience. By agreeing you accept the use of cookies in accordance with our cookie policy.

I accept I decline