echo $my_thoughts > /dev/null

Code, philosophy and scribblings

Implement Decorators in Javascript ES5

There’s an amazing pattern in Python, called function decorators. It’s a set of declarations at the top of a function definition, that slightly enhances how the function behaves without actually polluting the function definition with related logic. I find it a really neat way to define a function and to re-use these enhancements/additional logic across functions.

If you are like me, in love with decorators and now in the wildness of Javascript, missing them – here’s a quick way to implement decorators in JS. My solution here, uses underscore’s compose method. But it’s easy to whip up your own version of compose, if you don’t really want to use underscorejs.

Here’s the short version:

Say, you have a couple of decorators.

One, which logs the return value of the function it decorates.

Two, which makes sure the function is called exactly and only once, no matter how many times it gets invoked.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/* Decorator 1 */
var shouldLog = function(original_fn) {
  var decorated_fn = function() {
      var returnVal = original_fn.apply(this, arguments);
      // log the return value
      console.log(returnVal);
      return returnVal;
    }
    return decorated_fn;
}

/* Decorator 2 */
var runOnlyOnce = function(original_fn) {
  var isAlreadyRun = false;
  var decorated_fn = function() {
  // only execute if not already run
  if (!isAlreadyRun) {
          var returnVal = original_fn.apply(this, arguments);
          isAlreadyRun = true;
          return returnVal;
        }
    }
    return decorated_fn;
}

var myFunction = function() {
  // here goes your function definition
}

// now decorate it.
myFunction = _.compose(shouldLog, runOnlyOnce)(myFunction);

Or you can go for another version, if you aren’t really a fan of two liners.

1
2
3
var myLittleFunction = U.compose(shouldLog, runOnlyOnce)(function() {
  // here goes your function definition
});

All done! Now every time you call your function, it passes through the decorators around it.

The logic is pretty straightforward considering you are familiar with higher order functions.

1
2
3
4
5
6
7
8
9
10
11
12
myFunction  = f(x)

shouldLog = g(x) // higher order function where x = some function (f(x))

runOnlyOnce = h(x) // higher order function where x = some function (f(x))

chainedDecorator = _.compose(decoratorFunc1, decoratorFunc2) // h(g(x))

decoratedMyFunction = chainedDecorators(myFunc) // h(g(f(x)))

// to execute function with decorations
decoratedMyFunc(args);

Caution: Be cautious when you write a decorator that modifies the return value / parameters of the function. This could cause potential breakage of other decorators. Be careful when you decorate a method. Make sure decoratedMyFunction is properly bound to the intended context. E.g decoratedMyFunction.bind(MyClass)