Method decorators are a tool for reusing common logic. They are complementary to Object Oriented Programming. Decorators encapsulate responsibility shared by different objects.

Consider the following code:

function TodoStore(currentUser){  let todos = [];    function add(todo){    let start = Date.now();    if(currentUser.isAuthenticated()){      todos.push(todo);    } else {      throw "Not authorized to perform this operation";    }                let duration = Date.now() - start;    console.log("add() duration : " + duration);  }      return Object.freeze({    add  });  }

The intent of the add() method is to add new to-dos to the internal state. Beside that, the method needs to check the user authorization and log the duration of execution. These two things are secondary concerns and can actually repeat in other methods.

Imagine we can encapsulate these secondary responsibilities in functions. Then we can write the code in the following way:

function TodoStore(){  let todos = [];    function add(todo){    todos.push(todo);  }      return Object.freeze({     add:compose(logDuration,authorize)(add)   }); }

Now the add() method just adds the todo to the list. The other responsibilities are implemented by decorating the method.

logDuration() and authorize() are decorators.

A function decorator is a higher-order function that takes one function as an argument and returns another function, and the returned function is a variation of the argument function.
Reginald Braithwaite in Javascript Allongé

Log Duration

A common scenario is logging the duration of a method call. The following decorator logs the duration of a synchronous call.

function logDuration(fn){  return function decorator(...args){    let start = Date.now();    let result = fn.apply(this, args);    let duration = Date.now() - start;    console.log(fn.name + "() duration : " + duration);    return result;  }}

Notice how the original function was called — by passing in the current value of this and all arguments : fn.apply(this, args) .

Authorization

The authorize() decorator makes sure the user has the rights to execute the method. This decorator is more complex as it has a dependency on another object currentUser. In this case we can use a function createAuthorizeDecorator() to build the decorator. The decorator will execute the method only if the user is authenticated.

function createAuthorizeDecorator(currentUser){  return function authorize(fn){    return function decorator(...args){      if(currentUser.isAuthenticated()){        return fn.apply(this, args);      } else {        throw "Not authorized to execute " + fn.name + "()";      }    }  }}

Now we can create the decorator and pass-in the dependencies.

let authorize = createAuthorizeDecorator(currentUser);

compose()

Often we need to apply multiple decorators to a method. A simple way to do that is to call the decorators one after the other. Look at the next example:

function add() { }
let addWithAuthorize = authorize(add);let addWithAuthorizeAndLog = logDuration(addWithAuthorize);addWithAuthorizeAndLog();

Another way is to compose all decorators and then apply the new composed decorator to the original function. We can use the compose() function from libraries like underscore.js.

Function composition is applying one function to the result of another.

Applying f() to the result of g() means compose(f,g)(x) and is the same as f(g(x)).

Composition works best with unary functions. Our decorators are unary functions.

A unary function is a function that takes one argument.

let composedDecorator = _.compose(logDuration, authorize);let addWithComposedDecorator = composedDecorator(add);addWithComposedDecorator();

Below you can see how compose() is used to apply decorators to the add() method.

function TodoStore(){  function add(){}      return Object.freeze({     add:_.compose(logDuration,authorize)(add)   });  }
let todoStore = TodoStore();todoStore.add();

Order

In some cases, the order in which decorators get executed may not be important. But often the order does matter.

In our example, they are executed from left to right:

  • 1st — the duration log starts
  • 2rd — the authorization is performed
  • 3rd — the original method is called

Factory Functions

I favor factory functions over classes for the flowing reasons:

  • Encapsulation. Members are private by default. I can decide what methods to expose in the public API. In classes all members are public.
  • There are no issues with this losing context. Factory functions don’t use this so there are no more related problems.
  • Objects have a better security. The internal state is encapsulated and the API is immutable. For example, a global object defined with class can be modified from the Developer Console.

For a more in-depth comparison, take a look at Class vs Factory function: exploring the way forward.

Remove the self-executing part from the Reveling Module Pattern and you get a factory function. Below is the definition of the TodoStore factory function.

function TodoStore(){    function get(){}  function add(todo){ }  function edit(id, todo){}  function remove(id){}      return Object.freeze({      get,      add,      edit,      remove  });  }
let todoStore = TodoStore();

Decorating the Factory Function

It’s often the case that we need to apply decorators to all public methods of an object. We can create the decorateMethods() function to apply decorators to all public methods of the factory function.

function decorateMethods(obj, ...decorators){  function decorateMethod(fnName){    if(typeof(obj[fnName]) === "function"){      obj[fnName] = _.compose(...decorators)(obj[fnName]);    }  }  Object.keys(obj).forEach(decorateMethod);  return obj}
function decorateAndFreeze(obj, ...args) {  var newObj = { ...obj };  decorateMethods(newObj, ...args);  return Object.freeze(newObj);}

Now we can use the decorateAndFreeze() function to decorate all public methods of the factory function.

function TodoStore(){    function get(){}  function add(todo){ }  function edit(id, todo){}  function remove(id){}      return decorateAndFreeze({      get,      add,      edit,      remove  }, logDuration, authorize);  }
The decorators act like keywords or annotations, documenting the method’s behaviour but clearly separating these secondary concerns from the core logic of the method.
Reginald Braithwaite in Javascript Allongé

Preserve the method’s context

The decorator should preserve the original method’s context. In short it should not loose the value of this. There are two rules to follow :

  • the decorator should return a function using the function keyword (don’t use the arrow syntax)
  • the original function should be called with call(this, …) or apply(this, …)

If we fail to do that, the decorated method will lose the context of this .

Factory functions don’t use this so they will work.

Classes, Constructor Functions, and Object literals use this so they will lose context.

Now look at the following example:

let logDuration = fn => (...args) => {    let start = Date.now();    let result = fn(...args);    let duration = Date.now() - start;    console.log(fn.name + "() duration : " + duration);    return result;  }
//Constructor functionfunction Service(){  this.url = "http://";}Service.prototype.fetch = logDuration(function fetch(){   console.log(this.url); //undefined  });
let service = new Service();service.fetch();
//Object literallet anotherService =  {  url : "http://",  fetch : logDuration(function fetch(){   console.log(this.url); //undefined  })}
anotherService.fetch();

Conclusion

Factory functions and decorators are powerful tools in our toolbox. Factory functions build OOP Objects. Decorators encapsulate common logic reusable between these objects. The two concepts are complementary.

You can also find decorators in popular libraries. Look at Here are a few function decorators you can write from scratch for examples.

Decorators improve readability. The code is much cleaner as it focuses on its main responsibility without being blurred by the the secondary logic.

Discover Functional JavaScript was named one of the best new Functional Programming ebooks by BookAuthority!

For more on applying functional techniques to React take a look at Functional React.

Learn how to apply the Principles of Design Patterns.

You can find me on Medium and Twitter.