Decorators - what are they and how to use them?
Decorators are expressions (functions) taking
descriptors as arguments and allowing to modify classes and properties using declarative syntax at design time. It sounds interesting, but doesn’t say much what exactly it does. Let’s check some basic example and examine what these arguments are and how to apply the decorator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
The decorator function is invoked obviously before the decorated method’s body. To apply decorator you need to preceed the name of the decorator with
@ (you can also pass some arguments there, but we will discuss it later). What about the arguments in the decorator functions? Basically,
target is the object whose property is being decorated,
name is, well, name of the property and
descriptor is the property descriptor. The most interesting key in the descriptor is
value which has the reference to the decorated function. That means we can modify the original function! Let’s say we have some heavy computation going on in some function and we want to log how long it takes to do something. That basically means we want to have some time references before executing and after executing function so that we can subtract one from another. Well, as we have reference to the original function that’s going to be pretty easy, we can just define new function and call the original one from there! Let’s check this out:
1 2 3 4 5 6 7 8 9 10 11 12
And that’s it! We extended the original function in a really elegant and unobtrusive manner. Notice that we still pass the arguments to the original function with JS
arguments. If you are familiar with Python, you’ve probably been using the decorators in the similar way. The cool thing is that syntax (
@) is even the same :).
Decorators can also be applied to classes. However, In that case we won’t have name and descriptor, only the first argument -
target - which is going to be the constructor of the class. So what are the use cases for decorating classes?
How about implementing mixins? An ideal interface would be something like
@mixin(myAwesomeFunctions). It happens that classes are just a sugar and adding instance functions is just defining properties onto the
prototype. We have a reference to the constructor, so if we create e.g.
User class, we can add new functions to its prototype the following way:
mixin decorator taking arguments? Not a problem, we just need to define function which returns another function that will be applied:
1 2 3 4 5 6
And that’s how we can use it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Quite powerful. Let’s see if we can do anything interesting in Ember with the decorators.
Decorators and Ember
To get started with decorators you just need to enable them in
1 2 3 4 5 6 7 8 9 10 11
The awesome thing is that we can use decorators for computed properties syntax! It just requires installing ember-computed-decorators addon. Let’s see what kind of benefits this addon has to offer. Imagine we have some
User model with
lastName properties and we want to add
fullName computed property:
1 2 3 4 5 6 7 8 9 10 11 12
But it just doesn’t look right,
property called on function may seem a bit magical. Well, we can use
1 2 3
but that’s pretty heavy. Also using
this.get is far from ideal in both cases. How about defining normal function taking some arguments and applying a decorator?
1 2 3 4 5 6 7 8 9 10 11 12 13 14
This looks perfect now. There are plenty of more computed decorators implemented in
ember-computed-decorators addon, I encourage you to check all of them.
The only problem is that the new syntax doesn’t play nicely with JSHint. The policy of JSHint is to support features that are Stage 2 of standardization process, which is not the case yet for decorators. The current workaround is to add some extra config to
1 2 3
and use computed decorators the following way:
1 2 3 4 5 6 7 8 9 10
Next time we are going to explore