Add the script to your page as a standalone library OR after jQuery/angular if you plan to use it as a plugin. You can download it from the dist folder.
<!-- standalone-->
<script src="../aop-0.5.3.min.js"></script>
<!-- OR as a jQuery plugin -->
<script src="../jquery-aop-0.5.3.min.js"></script>
angular code please review: http://jsbin.com/OyadaBu/3/edit
Functionallity is accesible in the global variable AOP if used as a standalone library or in $.aop if used as a jQuery plugin.
An aspect definition takes a target function and some advices, returning a new function with the new expected behaviour.
// Target function
function myFunction() {
console.log("myFunction");
}
// Advice
function myAdvice() {
console.log("myAdvice");
}
// Aspect
var myProxy = AOP.aspect(myFunction).after(myAdvice); // standalone
// OR as a jQuery plugin
// var myProxy = $.aop.aspect(myFunction).after(myAdvice);
myProxy();
myFunction
myAdvice
Advices can be of type before, after, afterReturning, afterThrowing and around. Some jQuery friendly aliases are also provided: complete (after), success (afterReturning) and error (afterThrowing). In the next example both proxies have exactly the same beahaviour
var myProxy1 = AOP.aspect(myFunction).after(myAdvice);
var myProxy2 = AOP.aspect(myFunction).complete(myAdvice);
Aspect definitions can also take a target object method. In that case we provide also the target object to allow the normal use of this inside the method.
var myObject = {
myMethod: function () {
// do something
}
}
var myProxy = AOP.aspect(myObject.myMethod, myObject).before(myAdvice);
When aspect definitions take just a target object all the methods in the object get decorated.
var myObject = {
myMethod: function () {
console.log("myMethod");
}
myOtherMethod: function () {
console.log("myOtherMethod");
}
}
function myAdvice() {
console.log("myAdvice");
}
AOP.aspect(myObject).after(myAdvice);
myObject.myMethod();
myObject.myOtherMethod();
myMethod
myAdvice
myOtherMethod
myAdvice
Advices can be chained, being the closest to the target function in the definition also the closest in the execution stack.
var myProxy = AOP.aspect(myFunction)
.before(myAdvice1)
.afterReturning(myAdvice2)
.afterThrowing(myAdvice3);
Advices can also be precompiled, but it is just a matter of taste, when not precompiled, advices cache their own proxy.
var myAdvice = AOP.around(function(target, args) {
var retval;
// do something before
try {
retval = target.apply(this, args); // target invocation
// do something after returning
} catch (e) {
// do something after throwing
throw e;
} finally {
// do something after
}
return retval
});
var myProxy = AOP.aspect(myFunction).advice(myAdvice);
In the previous example an around advice is used to do something before and after a target function. It should be noted that the target function invocation is handled in pure javascript syntax to keep things as simple as possible.
Other types of advices have access to target, args and retval too.
var add = AOP.aspect(function (arg1, arg2) {
return arg1 + arg2;
}).before(function (target, args) {
console.log(args[0] + " + " + args[1] + " = ");
}).after(function (target, args, retval) {
console.log(retval);
}),
returnValue = add(2, 3); // returnValue == 5
2 + 3 = 5
Every advice definition method can take a variable number of functions.
var myProxy = AOP.aspect(myFunction)
.after(myAdvice1, myAdvice2),
myProxy2 = AOP.aspect(myFunction)
.after(myAdvice1)
.after(myAdvice2);
Both calls in the previous example produce the same output. Also remember that myAdvice1 will be invoked before myAdvice2 as the closest advice to the aspect will create the inner most proxy in the execution stack.
As stated earlier, precompiling the advices is not a matter of performance but a matter of taste. Still it is rather useful to create advice compositions.
var aroundComposite = AOP.advice()
.before(myAdvice1)
.afterReturning(myAdvice2)
.afterThrowing(myAdvice3)
.after(myAdvice4)
.around(myAdvice5),
// alternative syntax
beforePrecompiled = AOP.before(myAdvice1),
afterReturningPrecompiled = AOP.afterReturning(myAdvice2),
afterThrowingPrecompiled = AOP.afterThrowing(myAdvice3),
afterPrecompiled = AOP.after(myAdvice4),
aroundPrecompiled = AOP.around(myAdvice5),
aroundPrecompiledComposite = AOP.advice(beforePrecompiled,
afterReturningPrecompiled,
afterThrowingPrecompiled,
afterPrecompiled,
aroundPrecompiled),
myProxy = AOP.aspect(myFunction).advice(aroundComposite),
myProxy2 = AOP.aspect(myFunction).advice(aroundPrecompiledComposite);
In the previous example both the aroundComposite and the aroundPrecompiledComposite have the same behaviour, they are just syntactic alternatives to create a precompiled advice with the compound behaviour of all the provided advices.
Another advanced technique would be the use of currying to create parameterized advices.
function logAdvice(logger) {
return logger.log("logAdvice");
}
var consoleLogAdvice = logAdvice.curry(console),
myProxy = AOP.aspect(myFunction)
.after(consoleLogAdvice);
myProxy();
logAdvice