Aspect Oriented Programming in JavaScript

Embed Size (px)

Citation preview

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    1/14

    Something Somewhere

    Exploring Software Development and Computer Science

    Aspect Oriented Programming in JavaScript

    Aspect Oriented Programming, JavaScript, Programming Languages June 23, 2013 1Comment

    Contents

    Aspect Oriented ProgrammingJavaScript Library for Aspect Oriented Programmingbefore adviceafter adviceafterThrowing adviceafterReturning advicearound adviceIntroducing methodsLibrary Implementation DetailsConclusion

    Aspect Oriented Programming

    You are probably already familiar with Object Oriented Programming (OOP) and FunctionalProgramming (FP) paradigms, Aspect Oriented Programming (AOP) is just another programmingparadigm. Lets quickly recall what OOP and FP mean and then define what AOP is.

    In OOP programs are constructed from objects: encapsulated data is guarded against access by othercode and has a few methods aached 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 oreturned from functions, ideally there is no shared state and each function communicates with otherfunctions just by means of its return values (output) and parameters (inputs).

    About these ads (hp://en.wordpress.com/about-these-ads/)

    ct Oriented Programming in JavaScript | Something Somewhere http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented

    4 8/2/2014

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    2/14

    AOP in its turn is centered around aspects. Aspect is a module of code that can be specified (adviced) aexecutable at a certain place in the rest of the code (pointcut). A good introduction into AOP for Java isgiven in the Spring framework documentation (hp://static.springsource.org/spring/docs/2.5.5/referenc/aop.html). Here are the definitions of the key terms in AOP:

    Aspect module of code to be executedAdvice specification when an aspect should be executedPointcut 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 theObject Oriented and Functional ways of doing things, and usually it is a mix of both. Some languagesdefinitely support one paradigm beer than another or do not support some paradigms at all: as anexample, Java is primarily an OOP language and Haskell is a FP language.

    Ultimately these are just different approaches at how one can structure their programs and they havetheir strong and weak points in certain situations. OOP is fine when we have a complex domain areawith many entities and relations between them and we would like to reflect 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 mancomputations and can make a good use of its ability to decompose a complex algorithm into simplerreusable parts that are easy to comprehend. AOP is well-suited for cases when we want to introducesome new additional universal behavior, such as logging, transaction or error handling. In that case wewould like this additional functionality orthogonal to the core logic of our application to be put into onplace and not be scaered 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 addsupport for AOP to JavaScript by writing a simple library. I was inspired by the article by DanneLundqvists Aspect Oriented Programming and javascript (hp://www.dotvoid.com/2005/06/aspect-oriented-programming-and-javascript/) and just decided to implement my own library which willsupport a few more advices and will provide a different API.

    JavaScript allows to easily redefine methods, add properties to objects during execution time, and alsofunctions are objects in JavaScript. As a result a full-fledged Aspect Oriented Framework in JavaScript ionly about 150 lines long as you will shortly see. The topic may be a bit advanced for a beginnerJavaScript 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 mucfun. If this sounds like something completely new, please, refer to the Eloquent JavaScript(hp://eloquentjavascript.net/) book by Marijn Haverbeke.

    Our library will support the following:

    Aspect just functions, as almost everything in JavaScript is a functionAdvice before before method, after after method, afterThrowing when an exception is

    ct Oriented Programming in JavaScript | Something Somewhere http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented

    4 8/2/2014

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    3/14

    thrown, afterReturning just before returning a value from a function, around at the moment offunction executionPointcut methods all the methods of an object, prototypeMethods all the methods defined onthe 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:

    In order to advice application of the aspect beforeCallbackbefore invocation of each method defined othe prototype of Objectwe call jsAspect.injectwith appropriate arguments:

    1234

    56789

    10111213141516

    1718192021222324252627

    test("jsAspect.inject: 'before' advice, 'prototypeMethods' pointcut", functi functionObject() {

    };

    Object.prototype.method1 = function() {

    return"method1value";

    };

    Object.prototype.method2 = function() {

    return"method2value";

    };

    jsAspect.inject(Object, jsAspect.pointcuts.prototypeMethods, jsAspect.ad functionbeforeCallback() {

    varargs = [].slice.call(arguments, 0);

    this.beforeCallbackArgs = this.beforeCallbackArgs || [];

    this.beforeCallbackArgs.push(args);

    }

    );

    varobj = newObject();

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

    equal(obj.method2("arg3", "arg4", "arg5"), "method2value", "method2 was

    deepEqual(obj.beforeCallbackArgs, [["arg1", "arg2"], ["arg3", "arg4", "a

    });

    ct Oriented Programming in JavaScript | Something Somewhere http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented

    4 8/2/2014

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    4/14

    As a result the function beforeCallbackis executed before each method with that methods argumentseach time it is invoked. In the callback function thisrefers to the object on which the original methodwas called. In this example we just check if there is an array of arguments defined on that object. If theris no array we create it and then record all the current arguments there. The test above does not actuallyverify that the execution happens before the method but it is also easy to check, this can be a simpleexercise for the reader.

    after advice

    after advice is quite similar to before, the difference is only in one argument to jsAspect.injectandthe fact that the aspect will actually be executed after the original method not before it:

    afterThrowing advice

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

    12345678

    jsAspect.inject(Object, jsAspect.pointcuts.prototypeMethods, jsAspect.advices functionbeforeCallback() {

    varargs = [].slice.call(arguments, 0);

    this.beforeCallbackArgs = this.beforeCallbackArgs || [];

    this.beforeCallbackArgs.push(args);

    }

    );

    12345678

    jsAspect.inject(Object, jsAspect.pointcuts.prototypeMethods, jsAspect.advices functionafterCallback() {

    varargs = [].slice.call(arguments, 0);

    this.afterCallbackArgs = this.afterCallbackArgs || [];

    this.afterCallbackArgs.push(args);

    }

    );

    ct Oriented Programming in JavaScript | Something Somewhere http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented

    4 8/2/2014

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    5/14

    Also we see here that a few aspects can be applied for the same advice. In fact this is true not only forafterThrowing but for types of supported advices. In the aspect we just append a suffix to theexception message and then verify that the exceptions are still rethrown from the original methods withthe modified as expected messages.

    123456789

    1011121314151617181920212223242526272829303132

    33343536

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

    };

    Object.prototype.method1 = function() {

    thrownewError("method1exception");

    };

    Object.prototype.method2 = function() {

    thrownewError("method2exception");

    };

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

    functionafterThrowingCallback(exception) {

    exception.message = exception.message + "_aspect1"

    }

    ); jsAspect.inject(Object, jsAspect.pointcuts.prototypeMethods, jsAspect.ad

    functionafterThrowingCallback(exception) {

    exception.message = exception.message + "_aspect2"

    }

    );

    varobj = newObject();

    varthrownExceptions = [];

    ["method1", "method2"].forEach(function(methodName) {

    try{

    obj[methodName]();

    } catch(exception) {

    thrownExceptions.push(exception.message);

    }

    });

    deepEqual(thrownExceptions, ["method1exception_aspect2_aspect1", "method

    });

    ct Oriented Programming in JavaScript | Something Somewhere http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented

    4 8/2/2014

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    6/14

    afterReturning advice

    This advice is applied when the original function is about to return its value. Then this value is passed aan 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:

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

    around advice

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

    123456789

    101112131415161718

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

    };

    Object.prototype.identity = function(value) {

    returnvalue;

    };

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

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

    functionafterReturningCallback(retValue) {

    returnretValue + "_"+ aspectName;

    }

    );

    });

    equal(newObject().identity("value"), "value_aspect3_aspect2_aspect1", "});

    ct Oriented Programming in JavaScript | Something Somewhere http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented

    4 8/2/2014

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    7/14

    Introducing methods

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

    123456789

    1011121314151617181920212223

    test("jsAspect.inject: 'around' advice, 'prototypeMethods' pointcut", functi functionObject() {

    };

    Object.prototype.identity = function(x) {

    returnx;

    };

    Object.prototype.minusOne = function(x) {

    returnx - 1;

    };

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

    functionaroundCallback(func, x) {

    return2 * func(x);

    }

    );

    varobj = newObject();

    equal(obj.identity(3), 6, "'around' advice has been applied to 'identity

    equal(obj.minusOne(3), 4, "'around' advice has been applied to 'minusOne

    });

    ct Oriented Programming in JavaScript | Something Somewhere http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented

    4 8/2/2014

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    8/14

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    9/14

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    10/14

    This function is really the core of the whole framework and implements the main idea behind it. Letslook into the details of what happens here. First we store the original method in the variableoriginalMethodand then redefine it. In the redefined method we first apply the before advises beforthe original method, then around advises at the time of execution of the original method, then try tocatch an exception and if we do catch an exception, then we re-throw it after calling advises registered thandle exceptions with afterThrowing advice. Then after advises are applied and thenafterReturing advises. Prey transparent and easy, just what one would expect to find in theimplementation.

    Then we create additional system fields on the original method to store the arrays of advises for each othe possible advice names. Also, at the end we push a wrapped original method as the first advice to th

    array of around advises in order not to deal with border conditions in other functions that will followbelow.

    Now lets look at the advice application methods:

    123456789

    101112131415161718192021222324

    functionenhanceWithAdvices(target, methodName) { varoriginalMethod = target[methodName];

    target[methodName] = function() {

    varself = 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);

    throwexception;

    };

    applyAfterAdvices(self, method, args); returnapplyAfterReturningAdvices(self, method, returnValue);

    };

    allAdvices.forEach(function(advice) {

    target[methodName][jsAspect.advices[advice]] = [];

    });

    target[methodName][jsAspect.advices.around].unshift(wrapAroundAdvice(ori

    };

    ct Oriented Programming in JavaScript | Something Somewhere http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented

    14 8/2/2014

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    11/14

    The implementation is again prey straightforward. We use forEachto iterate over the before, afterand afterThrowing advices, and reduceto apply the afterReturing advices in a sequence.

    around advices are handled a bit differently: we take the first advice (which as you remember was

    123456789

    1011121314151617181920212223242526272829303132

    333435363738394041

    functionapplyBeforeAdvices(context, method, args) { varbeforeAdvices = method[jsAspect.advices.before];

    beforeAdvices.forEach(function(advice) {

    advice.apply(context, args);

    });

    };

    functionapplyAroundAdvices(context, method, args) { vararoundAdvices = method[jsAspect.advices.around]

    .slice(0, method[jsAspect.advices.around].length),

    firstAroundAdvice = aroundAdvices.shift(),

    argsForAroundAdvicesChain = args.slice();

    argsForAroundAdvicesChain.unshift(aroundAdvices);

    returnfirstAroundAdvice.apply(context, argsForAroundAdvicesChain);

    };

    functionapplyAfterThrowingAdvices(context, method, exception) { varafterThrowingAdvices = method[jsAspect.advices.afterThrowing];

    afterThrowingAdvices.forEach(function(advice) {

    advice.call(context, exception);

    });

    };

    functionapplyAfterAdvices(context, method, args) { varafterAdvices = method[jsAspect.advices.after];

    afterAdvices.forEach(function(advice) {

    advice.apply(context, args);

    });

    };

    functionapplyAfterReturningAdvices(context, method, returnValue) { varafterReturningAdvices = method[jsAspect.advices.afterReturning];

    returnafterReturningAdvices.reduce(function(acc, current) {

    returncurrent(acc);

    }, returnValue);

    };

    ct Oriented Programming in JavaScript | Something Somewhere http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented

    14 8/2/2014

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    12/14

    wrapped earlier) and pass to it the arguments of the original method together with an array of all thearound advices as the first argument. For what happens next when executing the first wrappedadvice we have to look at the implementation of the wrapAroundAdvicemethod:

    When invoking a wrapped advice we check if there are any next around advises left (the last one isthe original method if you remember). If there are no advises we deal with the original method whichwe just invoke by passing to it the arguments provided by user at the point of the invocation of themethod enhanced with aspects. Otherwise we unwrap the next available around advice. Theessence of unwrapping is that we remove the extra argument containing the array of all the remaininaround advises. Then we pass this unwrapped advice to the current advice as the first argumentso that each around advice (except for the original method) has one argument more than the originalmethod: on the first place goes the next advice to be applied or the original method. Then execution of

    the current advice can triggerexecution of the next available around advice (first argument) and we will again recursively go to thefunction wrapAroundAdvice.

    Arguably wrapAroundAdviceis the most complicated piece of the framework. It looks like there arealternatives to emulating stack of calls in this way, for example, we can redefine the function each time new around advice is added. But then we would have to copy all the advises bound to the originalmethod to the new method and this new redefined method will have to have a structure like the methowe create in enhanceWithAdvices, and then things get again a bit complicated when we want to addanother around advice as we would not like to execute each before advice more than once, we will

    123456789

    10111213141516

    17181920212223

    functionwrapAroundAdvice(advice) { varoldAdvice = advice,

    wrappedAdvice = function(leftAroundAdvices) {

    varoThis = this,

    nextWrappedAdvice = leftAroundAdvices.shift(),

    args = [].slice.call(arguments, 1);

    if(nextWrappedAdvice) {

    varnextUnwrappedAdvice = function() {

    varargsForWrapped = [].slice.call(arguments, 0);

    argsForWrapped.unshift(leftAroundAdvices);

    returnnextWrappedAdvice.apply(oThis, argsForWrapped);

    };

    args.unshift(nextUnwrappedAdvice);

    };

    returnoldAdvice.apply(this, args);

    };

    //Can be useful for debugging

    wrappedAdvice.__originalAdvice = oldAdvice;

    returnwrappedAdvice;

    };

    ct Oriented Programming in JavaScript | Something Somewhere http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented

    14 8/2/2014

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    13/14

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

    Conclusion

    We demonstrated that it is quite easy to implement a full-fledged AOP framework in JavaScript due tothe dynamic nature of the language and standard extensibility possibilities it provides. The createdframework implements some of the functionality (advises) of the Java based Spring framework(hp://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 fewwords of caution should be added. AOP is a quite powerful tool and can decouple code modules wellfrom each other, but the problem is that quite often this decoupling is excessive and introducesadditional 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 oto always try to solve a particular problem using the traditional programming paradigms like OOP andFP and only to resort to AOP when it is really needed. For example, it is much beer to introducelogging in one place of your application with one aspect than to add repetitive logging calls all over theplace, AOP is of a great use here. That is, knowing some paradigm does not mean that it is alwayswell-suited for solving your problem. This is almost the same as with design paerns, remember themain goal is to write an application fast and easy so that it is maintainable rather than making your codlook smart. Abusing both design paerns and AOP is certainly discouraged. Following this advice (npun intended) will save a lot of time for people who will be supporting your code in the future.

    And finally, the full implementation of the library can be found here:jsAspect: Aspect Oriented

    Framework for JavaScript (hps://github.com/antivanov/jsAspect)

    Links

    Article Aspect Oriented Programming and JavaScript (hp://www.dotvoid.com/2005/06/aspect-oriented-programming-and-javascript/)

    Aspect Oriented Programming with Spring (Java) (hp://static.springsource.org/spring/docs/2.5.5/reference/aop.html)

    ct Oriented Programming in JavaScript | Something Somewhere http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented

    14 8/2/2014

  • 8/9/2019 Aspect Oriented Programming in JavaScript

    14/14

    You May Like

    1.

    Tagged: AOP (hp://smthngsmwhr.wordpress.com/tag/aop/), Aspect Oriented Programming(hp://smthngsmwhr.wordpress.com/tag/aspect-oriented-programming/), JavaScript(hp://smthngsmwhr.wordpress.com/tag/javascript/), jsAspect (hp://smthngsmwhr.wordpress.com/tag/jsaspect/)

    One thought on Aspect Oriented Programming

    in JavaScript

    Aspekt orientierte Programmierung | Konstantin Krassmanns BlogApril 3, 2014 at 7:05 pm Reply[] jsAspects: Der garnicht so alte Blogpost beschreibt ein relativ mchtiges Framework, dass im

    Prinzip alles umset. Die Syntax ist fr den Anfang sehr kryptisch. Eventuell liefere Ich noch eineImplemtation im JSFiddle. []

    1.

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

    Follow

    Follow Something Somewhere

    Powered by WordPress.com

    ct Oriented Programming in JavaScript | Something Somewhere http://smthngsmwhr.wordpress.com/2013/06/23/aspect-oriented