This article originally appeared on dormoshe.io
Decorators are one of my favorite features in ECMAScript. The ease of use and easy to build make them so popular. They are clean. They are simple. They are cool. They are here to stay. Now you can use them, and you don’t want to ignore them. Most of the developers use decorators, but they don’t know what they are and how to build them.
The first time I really met the decorators was when Angular 2 was landed, in September 2016 — do you remember? 😍. They look at me, I look on them and it was love at first sight.
In this article, we will understand what they are, how to build them and why we don’t need to be afraid of them. We will see how to build the rich logger decorators from my previous article.
One moment, what is a Decorator?
A decorator is just a function.
Decorators make it possible to annotate and modify classes and properties at design time.
A Decorator is a special kind of declaration that can be attached to a class declaration, method, property, or parameter. Decorators use the form @expression, where expression must evaluate to a function that will be called at run-time with information about the decorated declaration.
In this section we will see how to build some parts of the rich-logger-decorator project.
The @Logger decorator is a method decorator. Log messages will be printed before the function will start and after the function will end. This decorator can get options that define the behavior of the flow.
Because of the options argument, this decorator needs to return a function (so this is a decorator factory). A method decorator has a signature like that
function(target, methodName, descriptor).
It takes 3 arguments:
- target — the original method being decorated.
- methodName — the name of the method being decorated.
- descriptor — a property descriptor of the given property if it exists on the object, undefined otherwise. The property descriptor is obtained by invoking the Object.getOwnPropertyDescriptor() function.
The first part of the function tries to fetch the descriptor, for some edge cases (for the ClassLogger decorator):
The second part is the fetching of the original method by accessing the value of the descriptor:
The third part is the monkey patch of the original method:
As you can see one message is being logged by the mechanism before the original method call and one after the call. The function called with the source arguments call and the result returned by the new method.
In the last part, we mark the method as monkey patched method (will be used later in the ClassLogger decorator) and return the descriptor to support using of more decorators for this method.
The @ClassLogger decorator is a method decorator. When you put the decorator on top of the class definition, all the methods in the class are logged automatically (except methods that are decorated by @DisableLogger).
As Logger decorator, this is also a decorator factory. A class decorator has a signature like that
function(target). The target is a reference to the class constructor.
The decorator needs to decorate the requested methods — all the class methods being decorated, unless the loggedMethodsNames array option will be set. After it’s set — only the methods in the array will be decorated.
This code fetches the properties of the class (including the class methods). Then they were filtered using the loggedMethodsNames array option.
Now, loop over the filtered items. If the item is not a function or its already decorated by the logging mechanism (according to __loggerMonkeyPatchCompleted), the item is omitted.
And now we “monkey patched” these methods like in the method Logger decorator. That’s it.
The @DisableLogger decorator is also a method decorator. The ClassLogger decorator will skip this method from being logged.
For this purpose, we just need to change the boolean value and that’s it. This is an example for a simple decorator that doesn’t change the original method.
Decorators are amazing. They improve readability and reusability. You can compile them to ES5 with Typescript, Traceur or Bable. And now you know how to build them, so go and code your custom decorators and don’t forget to share…