Aspect-Oriented Programming in JavaScript

We all know about Object-Oriented Programming and Functional Programming, but have you heard about Aspect-Oriented Programming before?

We all know about Object-Oriented Programming, and we’ve probably, at least, heard of Functional Programming in the JavaScript world, but have you ever heard of Aspect-Oriented Programming? I know, it sounds like something you would hear in an episode of Power Rangers Mystic Force. However, AOP is a thing, and not only that, it’s a thing we’re not using and could use for several common use cases we see every day.

And the best part of it all is that just like with OOP and FP in JavaScript, you can use a mixture of AOP with FP or OOP without breaking a sweat. So let’s first understand what this aspect deal is, and how useful it can really be for JavaScript developers.

A brief introduction to AOP

Aspect-Oriented Programming provides a way for us to inject code into existing functions or objects, without modifying the target logic.

The injected code, although not required, is meant to have cross-cutting concerns, such as adding logging functionality, debugging metadata, or something less generic, but that could inject extra behavior without affecting the original code.

To give you a good example, imagine having written your business logic but now you realize that you have no logging code. The normal approach to this would be to centralize your logging logic inside a new module and the go function by function adding logging information.

However, if you could grab that same logger and inject it into every method you’re looking to log, at very specific points during their execution with a single line of code, then this would definitely give you a lot of value. Wouldn’t you agree?

Aspects, Advice, and Pointcuts or What, When, and Where

In order to formalize a bit the definition above, let’s take the example of the logger and cover these 3 concepts about AOP that are going to help you out if you decide to look further into this paradigm:

  • Aspects (What): These are the “aspects” or behavior you’re looking to inject into your target code. In our context (JavaScript), these will be functions that encapsulate the behavior you’re looking to add.

  • Advice (When): When do you want the aspect to run? They specify some common moments when you want your aspect’s code to be executed, such as “before”, “after”, “around”, “whenThrowing”, and the like. They, in turn, refer to the moment in time-related to the execution of the code. For the ones referring to after the code is executed, the aspects will intercept the returned value and potentially overwrite it if they needed to.

  • Pointcut (Where): They reference the place in your target code where you want to inject the aspect. In theory, you could pinpoint anywhere in your target code when you want your code to be executed. In practice, this is not that realistic, but you can potentially specify things such as: “all methods of my object”, or “only this particular method”, or we could even get fancy with something like “all methods starting with get_”.

With this explanation, you could argue that creating an AOP-based library to add logging logic to existing OOP-based business logic (for example) is relatively easy. All you’d have to do is replace the existing matching methods of the target object, with a custom function that would add the aspect’s logic at the right time and then call the original method.

A basic implementation

Just because I’m a visual learner, I think showing a basic example of how you’d go about implementing a sort of inject method to add AOP-based behavior would come a long way. The following example should clarify both, how easy it is to implement it and the type of benefits it brings to your code.

/** Helping function used to get all methods of an object */
const getMethods =(obj)=>Object.getOwnPropertyNames(Object.getPrototypeOf(obj)).filter(item