You are on page 1of 14

Aspect Oriented Programming in JavaScript | Something Somewhere

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

Something Somewhere
Exploring Software Development and Computer Science

Aspect Oriented Programming in JavaScript


Aspect Oriented Programming, JavaScript, Programming Languages
Comment
About these ads (h p://en.wordpress.com/about-these-ads/)

June 23, 2013

Contents
Aspect Oriented Programming
JavaScript Library for Aspect Oriented Programming
before advice
after advice
afterThrowing advice
afterReturning advice
around advice
Introducing methods
Library Implementation Details
Conclusion

Aspect Oriented Programming


You are probably already familiar with Object Oriented Programming (OOP) and Functional
Programming (FP) paradigms, Aspect Oriented Programming (AOP) is just another programming
paradigm. Lets quickly recall what OOP and FP mean and then dene what AOP is.
In OOP programs are constructed from objects: encapsulated data is guarded against access by other
code and has a few methods a ached to it that allow reading and modifying this data.
In FP the primary building block is function. Functions can be passed as arguments to other functions or
returned from functions, ideally there is no shared state and each function communicates with other
functions just by means of its return values (output) and parameters (inputs).
1 of 14

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

AOP in its turn is centered around aspects. Aspect is a module of code that can be specied (adviced) as
executable at a certain place in the rest of the code (pointcut). A good introduction into AOP for Java is
given in the Spring framework documentation (h p://static.springsource.org/spring/docs/2.5.5/reference
/aop.html). Here are the denitions of the key terms in AOP:
Aspect module of code to be executed
Advice specication when an aspect should be executed
Pointcut place in code where an advice should be applied
What all those words mean in practice will be more clear from the examples below.
These paradigms are not mutually exclusive, for example, in JavaScript it is possible to use both the
Object Oriented and Functional ways of doing things, and usually it is a mix of both. Some languages
denitely support one paradigm be er than another or do not support some paradigms at all: as an
example, Java is primarily an OOP language and Haskell is a FP language.
Ultimately these are just dierent approaches at how one can structure their programs and they have
their strong and weak points in certain situations. OOP is ne when we have a complex domain area
with many entities and relations between them and we would like to reect all those things in the code
base so that we can support and evolve our application easier. FP works well when we need to do many
computations and can make a good use of its ability to decompose a complex algorithm into simpler
reusable parts that are easy to comprehend. AOP is well-suited for cases when we want to introduce
some new additional universal behavior, such as logging, transaction or error handling. In that case we
would like this additional functionality orthogonal to the core logic of our application to be put into one
place and not be sca ered across the whole application.

JavaScript Library for Aspect Oriented Programming


As discussed above JavaScript supports both OOP and FP. It also turns out that it is very easy to add
support for AOP to JavaScript by writing a simple library. I was inspired by the article by Danne
Lundqvists Aspect Oriented Programming and javascript (h p://www.dotvoid.com/2005/06/aspectoriented-programming-and-javascript/) and just decided to implement my own library which will
support a few more advices and will provide a dierent API.
JavaScript allows to easily redene methods, add properties to objects during execution time, and also
functions are objects in JavaScript. As a result a full-edged Aspect Oriented Framework in JavaScript is
only about 150 lines long as you will shortly see. The topic may be a bit advanced for a beginner
JavaScript programmer, so it is assumed that the reader at this point has a good handle of prototypes,
closures, invocation context, and other advanced features that make programming in JavaScript so much
fun. If this sounds like something completely new, please, refer to the Eloquent JavaScript
(h p://eloquentjavascript.net/) book by Marijn Haverbeke.
Our library will support the following:

2 of 14

Aspect just functions, as almost everything in JavaScript is a function


Advice before before method, after after method, afterThrowing when an exception is

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

thrown, afterReturning just before returning a value from a function, around at the moment of
function execution
Pointcut methods all the methods of an object, prototypeMethods all the methods dened on
the prototype of an object, method only a single method of an object
Lets take a closer look and start from examples.

before advice
Simple example of using the AOP library:
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

test("jsAspect.inject: 'before' advice, 'prototypeMethods' pointcut", function


function Object() {
};
Object.prototype.method1 = function() {
return "method1value";
};
Object.prototype.method2 = function() {
return "method2value";
};

jsAspect.inject(Object, jsAspect.pointcuts.prototypeMethods, jsAspect.advices.


function beforeCallback() {
var args = [].slice.call(arguments, 0);
this.beforeCallbackArgs = this.beforeCallbackArgs || [];
this.beforeCallbackArgs.push(args);
}
);
var obj = new Object();

equal(obj.method1("arg1", "arg2"), "method1value", "method1 was called as expe


equal(obj.method2("arg3", "arg4", "arg5"), "method2value", "method2 was called
deepEqual(obj.beforeCallbackArgs, [["arg1", "arg2"], ["arg3", "arg4", "arg5"
});
In order to advice application of the aspect beforeCallback before invocation of each method dened on
the prototype of Object we call jsAspect.inject with appropriate arguments:

3 of 14

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

1
2
3
4
5
6
7
8

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

jsAspect.inject(Object, jsAspect.pointcuts.prototypeMethods, jsAspect.advices.befor


function beforeCallback() {
var args = [].slice.call(arguments, 0);
this.beforeCallbackArgs = this.beforeCallbackArgs || [];
this.beforeCallbackArgs.push(args);
}
);

As a result the function beforeCallback is executed before each method with that methods arguments
each time it is invoked. In the callback function this refers to the object on which the original method
was called. In this example we just check if there is an array of arguments dened on that object. If there
is no array we create it and then record all the current arguments there. The test above does not actually
verify that the execution happens before the method but it is also easy to check, this can be a simple
exercise for the reader.

after advice
after advice is quite similar to before, the dierence is only in one argument to jsAspect.inject and
the fact that the aspect will actually be executed after the original method not before it:
1
2
3
4
5
6
7
8

jsAspect.inject(Object, jsAspect.pointcuts.prototypeMethods, jsAspect.advices.after


function afterCallback() {
var args = [].slice.call(arguments, 0);
this.afterCallbackArgs = this.afterCallbackArgs || [];
this.afterCallbackArgs.push(args);
}
);

afterThrowing advice
This advice is executed when an exception occurs in the original method. Then this exception is rst
passed to the aspect as an argument and is still rethrown later in the original method. Example:

4 of 14

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

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
32
33
34
35
36

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

test("jsAspect.inject: 'afterThrowing' several aspects", function() {


function Object() {
};
Object.prototype.method1 = function() {
throw new Error("method1exception");
};
Object.prototype.method2 = function() {
throw new Error("method2exception");
};

jsAspect.inject(Object, jsAspect.pointcuts.prototypeMethods, jsAspect.advices.


function afterThrowingCallback(exception) {
exception.message = exception.message + "_aspect1"
}
);
jsAspect.inject(Object, jsAspect.pointcuts.prototypeMethods, jsAspect.advices.
function afterThrowingCallback(exception) {
exception.message = exception.message + "_aspect2"
}
);
var obj = new Object();
var thrownExceptions = [];
["method1", "method2"].forEach(function (methodName) {
try {
obj[methodName]();
} catch (exception) {
thrownExceptions.push(exception.message);
}
});

deepEqual(thrownExceptions, ["method1exception_aspect2_aspect1", "method2excep


});
Also we see here that a few aspects can be applied for the same advice. In fact this is true not only for
afterThrowing but for types of supported advices. In the aspect we just append a sux to the
exception message and then verify that the exceptions are still rethrown from the original methods with
the modied as expected messages.

5 of 14

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

afterReturning advice
This advice is applied when the original function is about to return its value. Then this value is passed as
an argument to the aspect and the actual return value will be whatever the aspect decides to return. A
few aspects can be applied at the same time as well:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

test("jsAspect.inject: several 'afterReturning' aspects", function() {


function Object() {
};
Object.prototype.identity = function(value) {
return value;
};

["aspect1", "aspect2", "aspect3"].forEach(function (aspectName) {


jsAspect.inject(Object, jsAspect.pointcuts.prototypeMethods, jsAspect.advi
function afterReturningCallback(retValue) {
return retValue + "_" + aspectName;
}
);
});

equal(new Object().identity("value"), "value_aspect3_aspect2_aspect1", "'after


});
In this example we create several named aspects and in each of them append the name of the aspect to
the return value.

around advice
The most interesting advice is around. Here aspect receives several arguments: the original function to
which the aspect was applied (can be actually just another aspect, but this is entirely hidden from the
current aspect) and the arguments of the original function. Then the return value of the aspect is
returned from the original function:

6 of 14

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

test("jsAspect.inject: 'around' advice, 'prototypeMethods' pointcut", function


function Object() {
};
Object.prototype.identity = function(x) {
return x;
};
Object.prototype.minusOne = function(x) {
return x - 1;
};

jsAspect.inject(Object, jsAspect.pointcuts.prototypeMethods, jsAspect.advices.


function aroundCallback(func, x) {
return 2 * func(x);
}
);
var obj = new Object();
equal(obj.identity(3), 6, "'around' advice has been applied to 'identity'"
equal(obj.minusOne(3), 4, "'around' advice has been applied to 'minusOne'"
});

Introducing methods
We can also easily add methods to existing objects like this:

7 of 14

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

test("jsAspect.introduce: 'methods' pointcut", function() {


function Object() {
this.field1 = "field1value";
this.field2 = "field2value";
};
Object.prototype.method1 = function () {
return "valuefrommethod1";
};
jsAspect.introduce(Object, jsAspect.pointcuts.methods, {
field3: "field3value",
staticmethod1: function () {
return "valuefromstaticmethod1";
}
});

equal(Object.field3, "field3value", "Object.prototype.field3");


equal(Object.staticmethod1 ? Object.staticmethod1() : "", "valuefromstaticmeth
});
Another pointcut jsAspect.pointcuts.methods is used here so that we introduce methods and elds not
to the objects prototype but to the object directly.

Library Implementation Details


Now it is time for the implementation details. The basic idea is very simple: we will redene each
method that is matched by the provided pointcut. The redened method will call the original method
after executing the advised aspects at appropriate points in time. For the end user there will be no
dierence with the original method in how the method should be called.
But lets rst start with something a bit simpler, the introduce method that adds new methods and elds
to an object:

8 of 14

1
2
3
4
5
6
7
8

jsAspect.introduce = function (target, pointcut, introduction) {


target = (jsAspect.pointcuts.prototypeMethods == pointcut) ? target.prototype :
for (var property in introduction) {
if (introduction.hasOwnProperty(property)) {
target[property] = introduction[property];
}
}
};

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

First an appropriate target is determined and then we copy each own property in introduction to this
target. It is that easy in JavaScript, no proxies, no additional objects, just plainly dening methods on the
original object.
The implementation of the main method we have seen in the examples so far jsAspect.inject:
1
2
3
4
5
6
7
8
9
10
11
12

jsAspect.inject = function (target, pointcut, adviceName, advice, methodName) {


if (jsAspect.pointcuts.method == pointcut) {
injectAdvice(target, methodName, advice, adviceName);
} else {
target = (jsAspect.pointcuts.prototypeMethods == pointcut) ? target.proto
for (var method in target) {
if (target.hasOwnProperty(method)) {
injectAdvice(target, method, advice, adviceName);
}
};
};
};

Again, we compute the correct target and then for each method in the target we inject a new advice as
specied by the provided adviceName. Next the function injectAdvice:
1
2
3
4
5
6
7
8
9
10
11
12

function injectAdvice(target, methodName, advice, adviceName) {


if (isFunction(target[methodName])) {
if (jsAspect.advices.around == adviceName) {
advice = wrapAroundAdvice(advice);
};
if (!target[methodName][adviceEnhancedFlagName]) {
enhanceWithAdvices(target, methodName);
target[methodName][adviceEnhancedFlagName] = true;
};
target[methodName][adviceName].unshift(advice);
}
};

If the target method in the object for some reason is not actually a function (just a usual value eld
instead), then we do nothing. Otherwise we check whether the method has already been enhanced to
support advices. If it has not yet been enhanced, we do enhance it, otherwise we simply add a new
advice to an additional system eld created on the original object by our framework. This eld contains
an array of all the advices that should be applied for a given adviceName. Also we can see that the
around advice requires some additional handling, here we just wrap it with wrapAroundAdvice
before actually adding it onto the system eld on the original method. It will become a bit clearer why
we do that after we consider the implementations of the functions enhanceWithAdvices and
wrapAroundAdvice.
Next, the function enhanceWithAdvices:

9 of 14

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

function enhanceWithAdvices(target, methodName) {


var originalMethod = target[methodName];
target[methodName] = function() {
var self = this,
method = target[methodName],
args = [].slice.call(arguments, 0),
returnValue = undefined;
applyBeforeAdvices(self, method, args);
try {
returnValue = applyAroundAdvices(self, method, args);
} catch (exception) {
applyAfterThrowingAdvices(self, method, exception);
throw exception;
};
applyAfterAdvices(self, method, args);
return applyAfterReturningAdvices(self, method, returnValue);

};
allAdvices.forEach(function (advice) {
target[methodName][jsAspect.advices[advice]] = [];
});
target[methodName][jsAspect.advices.around].unshift(wrapAroundAdvice(originalM
};

This function is really the core of the whole framework and implements the main idea behind it. Lets
look into the details of what happens here. First we store the original method in the variable
originalMethod and then redene it. In the redened method we rst apply the before advises before
the original method, then around advises at the time of execution of the original method, then try to
catch an exception and if we do catch an exception, then we re-throw it after calling advises registered to
handle exceptions with afterThrowing advice. Then after advises are applied and then
afterReturing advises. Pre y transparent and easy, just what one would expect to nd in the
implementation.
Then we create additional system elds on the original method to store the arrays of advises for each of
the possible advice names. Also, at the end we push a wrapped original method as the rst advice to the
array of around advises in order not to deal with border conditions in other functions that will follow
below.
Now lets look at the advice application methods:

10 of 14

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

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
32
33
34
35
36
37
38
39
40
41

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

function applyBeforeAdvices(context, method, args) {


var beforeAdvices = method[jsAspect.advices.before];
beforeAdvices.forEach(function (advice) {
advice.apply(context, args);
});
};
function applyAroundAdvices(context, method, args) {
var aroundAdvices = method[jsAspect.advices.around]
.slice(0, method[jsAspect.advices.around].length),
firstAroundAdvice = aroundAdvices.shift(),
argsForAroundAdvicesChain = args.slice();
argsForAroundAdvicesChain.unshift(aroundAdvices);
return firstAroundAdvice.apply(context, argsForAroundAdvicesChain);
};
function applyAfterThrowingAdvices(context, method, exception) {
var afterThrowingAdvices = method[jsAspect.advices.afterThrowing];
afterThrowingAdvices.forEach(function (advice) {
advice.call(context, exception);
});
};
function applyAfterAdvices(context, method, args) {
var afterAdvices = method[jsAspect.advices.after];
afterAdvices.forEach(function (advice) {
advice.apply(context, args);
});
};
function applyAfterReturningAdvices(context, method, returnValue) {
var afterReturningAdvices = method[jsAspect.advices.afterReturning];
return afterReturningAdvices.reduce(function (acc, current) {
return current(acc);
}, returnValue);
};

The implementation is again pre y straightforward. We use forEach to iterate over the before, after
and afterThrowing advices, and reduce to apply the afterReturing advices in a sequence.
around advices are handled a bit dierently: we take the rst advice (which as you remember was
11 of 14

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

wrapped earlier) and pass to it the arguments of the original method together with an array of all the
around advices as the rst argument. For what happens next when executing the rst wrapped
advice we have to look at the implementation of the wrapAroundAdvice method:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

function wrapAroundAdvice(advice) {
var oldAdvice = advice,
wrappedAdvice = function (leftAroundAdvices) {
var oThis = this,
nextWrappedAdvice = leftAroundAdvices.shift(),
args = [].slice.call(arguments, 1);
if (nextWrappedAdvice) {
var nextUnwrappedAdvice = function() {
var argsForWrapped = [].slice.call(arguments, 0);
argsForWrapped.unshift(leftAroundAdvices);
return nextWrappedAdvice.apply(oThis, argsForWrapped);
};
args.unshift(nextUnwrappedAdvice);
};
return oldAdvice.apply(this, args);
};
//Can be useful for debugging
wrappedAdvice.__originalAdvice = oldAdvice;
return wrappedAdvice;
};

When invoking a wrapped advice we check if there are any next around advises left (the last one is
the original method if you remember). If there are no advises we deal with the original method which
we just invoke by passing to it the arguments provided by user at the point of the invocation of the
method enhanced with aspects. Otherwise we unwrap the next available around advice. The
essence of unwrapping is that we remove the extra argument containing the array of all the remaining
around advises. Then we pass this unwrapped advice to the current advice as the rst argument
so that each around advice (except for the original method) has one argument more than the original
method: on the rst place goes the next advice to be applied or the original method. Then execution of
the current advice can trigger
execution of the next available around advice (rst argument) and we will again recursively go to the
function wrapAroundAdvice.
Arguably wrapAroundAdvice is the most complicated piece of the framework. It looks like there are
alternatives to emulating stack of calls in this way, for example, we can redene the function each time a
new around advice is added. But then we would have to copy all the advises bound to the original
method to the new method and this new redened method will have to have a structure like the method
we create in enhanceWithAdvices, and then things get again a bit complicated when we want to add
another around advice as we would not like to execute each before advice more than once, we will

12 of 14

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

have to clean-up the originally added advises, etc. So the present implementation seemed like a
reasonably simple although it does require some mental eort to understand.

Conclusion
We demonstrated that it is quite easy to implement a full-edged AOP framework in JavaScript due to
the dynamic nature of the language and standard extensibility possibilities it provides. The created
framework implements some of the functionality (advises) of the Java based Spring framework
(h p://static.springsource.org/spring/docs/2.5.5/reference/aop.html).
Now before you go and use AOP techniques in your code like the ones we used here I feel that a few
words of caution should be added. AOP is a quite powerful tool and can decouple code modules well
from each other, but the problem is that quite often this decoupling is excessive and introduces
additional complexity. As a result it may become virtually impossible to trace what causes what to
execute in your code and it can bring a lot of headache when later supporting this code. It really pays o
to always try to solve a particular problem using the traditional programming paradigms like OOP and
FP and only to resort to AOP when it is really needed. For example, it is much be er to introduce
logging in one place of your application with one aspect than to add repetitive logging calls all over the
place, AOP is of a great use here. That is, knowing some paradigm does not mean that it is always
well-suited for solving your problem. This is almost the same as with design pa erns, remember the
main goal is to write an application fast and easy so that it is maintainable rather than making your code
look smart. Abusing both design pa erns and AOP is certainly discouraged. Following this advice (no
pun intended) will save a lot of time for people who will be supporting your code in the future.
And nally, the full implementation of the library can be found here: jsAspect: Aspect Oriented
Framework for JavaScript (h ps://github.com/antivanov/jsAspect)

Links
Article Aspect Oriented Programming and JavaScript (h p://www.dotvoid.com/2005/06/aspectoriented-programming-and-javascript/)
Aspect Oriented Programming with Spring (Java) (h p://static.springsource.org/spring/docs/2.5.5
/reference/aop.html)

13 of 14

8/2/2014 6:21 PM

Aspect Oriented Programming in JavaScript | Something Somewhere

http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented-progra...

You May Like


1.

Tagged: AOP (h p://smthngsmwhr.wordpress.com/tag/aop/), Aspect Oriented Programming


(h p://smthngsmwhr.wordpress.com/tag/aspect-oriented-programming/), JavaScript
(h p://smthngsmwhr.wordpress.com/tag/javascript/), jsAspect (h p://smthngsmwhr.wordpress.com
/tag/jsaspect/)

One thought on Aspect Oriented Programming


in JavaScript
1. Aspekt orientierte Programmierung | Konstantin Krassmanns Blog April 3, 2014 at 7:05 pm Reply
[] jsAspects: Der garnicht so alte Blogpost beschreibt ein relativ mchtiges Framework, dass im
Prinzip alles umse t. Die Syntax ist fr den Anfang sehr kryptisch. Eventuell liefere Ich noch eine
Implemtation im JSFiddle. []

Create a free website or blog at WordPress.com. | The Delicacy Theme.


Follow

Follow Something Somewhere

14 of 14

Powered by WordPress.com

8/2/2014 6:21 PM

You might also like