The 5 ES7 Decorators I want to use, now.
With ES6 finalized in June, it is time to look forward to ES7. There are some awesome proposals that you should get excited about, including: Object.observe, async functions, and the new bind operator (<this>::<fn>). The future of javascript is even more exciting with the announcement of Web Assembly. All this mixed with V8, Node and Tessel, and we are seeing Javascript slowly begin to take over the world. And I don’t mind that at all.
One of the ES7 proposals I am most excited about is function/class decorators. Decorators are a fantastic way to keep your code clean by reusing similar functionality throughout your codebase. They are available right now via babel’s stage 0, and should be widely available in browsers in the next few years (assuming ES accepts the proposal). Exciting, right!?
Here are the 5 ES7 decorators I want to use, now.
- @require. Instead of parameter checking inside every function, let the decorator do it for you. It would be syntactic sugar for requiring non-falsy values from arguments
So instead of
function request(url, data) {
if (!url) return;
if (!data || !data.method) return;
...
}
You could write
@require('url', 'data.method')
function request(url, data) {
...
}
2. @debug. Way too often when writing code, we make mistakes and have to figure out WTF went wrong. That usually includes a few well placed console.log statements. This decorator could handle it all for you and output it all sexy-like. It could even use the old AngularJS DI trick of regexing for the names of the arguments.
@debug
function request(url, data) {
return new Promise((resolve, reject) => {
...
});
}// prints
> {
fn: 'request',
args: {
url: 'https://...',
data: {
method: 'GET',
...
}
},
returns: { ...<Promise>... }
}
3. @wrap. Much like inheritance, or extending from another class, the wrap decorator could give us a kind of pseudo inheritance at the function level. An important example I can see is wrapping non-promise based functions and converting them into promises. I really like the idea of this, since promise logic can quickly become very repetitive.
function promiseWrapper(fn, args) {
let callback;
return new Promise((resolve, reject) => {
for (let i = 0; i < args.length; i++) {
if (typeof args[i] === 'function') {
callback = args[i];
args[i] = resolve;
break;
}
}
fn.apply(null, args);
}).then(() => {
callback.apply(null, arguments)
});
}@wrap(promiseWrapper)
function request(url, data, callback) {
... new XMLHttpRequest
... callback()
}
4. @memoize. If you don’t know about memoization, you probably do and you just don’t know the fancy term. All it means is that if a function has been called with the same arguments, return the same value that was produced last time. This is a great speed improvement for a lot of processes, and is super easy to add with a decorator. It is very popular to use _.memoize currently, but it will be much cleaner once classes to come around to use a decorator.
@memoize
function request(url, data, callback) {
return new Promise((resolve, reject) => { ... });
}
5. @if. This is very interesting to me. It would give you the ability to turn any function into a noop based on some function. I see this being useful for dev vs prod log functions, or even noopifying (new word) certain functions when a user is not logged in. Here’s how we’d use it. I’d love to hear other ideas for how @if could be used.
@if(::User.isAuthenticated)
function getUserData() {
...
}
User.isAuthenticated is a function that we bound to the User object using the new ES7 bind operator for shorthand .bind() syntax.
6. And for a bonus decorator: @ngController. This is an oddly specific use case, but I can see it being widely adopted throughout different frameworks. Something like this would allow us to cleanly register functions/classes with a framework, and make it even easier to switch out different frameworks in the future. I actually am using this one in a curiosity-driven side project that you should check out, https://github.com/zackargyle/ng-es7. I use it to register modules and handle dependency injection.
@ngController(Const.APP, [‘User’])
class MainController {
constructor(User) {
this.User = User;
}
login() {
this.User.login();
}
}
There we go, that’s it. If you have any decorators that YOU are looking forward to using, ping me, I’d love to hear about it. The future is looking very bright for Javascript, so let’s keep being excited and doing awesome things.