The sources can be downloaded here.
A couple of days ago, some colleagues and I were discussing the ins and outs of JavaScript, and one of the things that came up as we talked was how handy having functions as first class objects really is. Although I’d (ab)used this feature several times in the past, I’d never really thought much about it. It was just one of those things that’s, well, taken for granted in the language. Thinking it over a bit, a niggling thought came to me – that it would be really easy to implement aspect oriented programming in JavaScript.
Ok, But why bother?
Apart from the fact that, if I hadn’t bothered, this post would have been really short, there are a few cases for AOP in JavaScript which look quite interesting to me. One of the most obvious is debugging. By applying aspects to a function, it is possible to see what’s going in and out, without messing about in the function itself. Even if it’s a simple “alert(argument1 != 0);” statement (yes I know it’s not the brightest way to debug something, yes I’m guilty of doing this too at times, and yes, I’m fairly sure everyone who does javascript has done it at some point in their career, so don’t give me that look), you can use an aspect to display the popup instead of messing around with your nice clean function. Since you can lump the insertion of various aspects together, you could also, conceivably, put all the insertions in a separate script file, include it when you’re debugging, and then remove it when you’re done – no more hunting down every last “alert(‘Extremely vulgar message I put in after hours of frustration.’)”.
The standard arguments for using AOP apply – you can maintain separation of concern, add new behaviours, and all that.
The final reason for bothering was, in my case, a very slow and boring evening. Hey, it’s my blog and I don’t need to justify my choice of subjects, k?
Caveats
The sources provided with this post, are not only bad for production use, but they are also fundamentally flawed as we shall discuss in a little while. If you intend to use them as they are, remember that I’m not taking any responsibility for them, am not likely to maintain them, etc etc. If you find a use for them and don’t mind the issues, you’re free to use them, play with them, or otherwise modify them as you see fit.
The idea
As we said above, functions are first class objects in JavaScript. This means that you can play with them more or less as you would with any other object; you can assign them, attach properties (or even other functions) to them, and so on. In fact, they’re pretty much run-of-the-mill objects. with the little extra twist that you can call them, with the usual function_name([arguments]) syntax to execute them.
Since they are so conveniently available, it’s possible to pass them as arguments to another function, which is what we’re doing in the Aspect(target, aspect, implementation) function. In the first argument, target, we state which function we want to apply an aspect to. In the second argument, aspect, we specify the aspect we want to apply to that function – in other words, the behaviour that will be added to the target function. The third argument, implementation, is used to define the sequencing of the call. This is, again, a function, which calls target and aspect in a given order.
The Aspect function can be called through one of three helper functions: AspectBefore, AspectOnSuccess, and AspectOnException. These three define a specific sequencing implementation. AspectBefore, for example, calls the aspect before calling the target.
When we’re applying an aspect, what we’re actually doing is, creating a new proxy function (the behaviour or source of which will be identical to the implementation argument), and attaching two properties to it. Since all javascript objects are associative arrays (think Dictionaries in the .net parlance), adding these properties is as simple as using the square bracket notation and assigning the values – in this case, the aspect, and a copy of the original function. Then, we replace the original function with the proxy.
Note the emphasis on the word copy. When we pass arguments in javascript, in many cases (i.e. anywhere we’re not passing primitive types) they will be passed by reference. If we assigned the reference to the proxy instead of a copy, we’d end up with a self-referencing object which would cause an infinite loop, and all ensuing hilarity.
Arguments, arguments …
Although we are now able to attach additional behaviours to the function, we still can’t do anything useful with them, because we have no way of controlling them. Without the ability to pass arguments to them, aspects would be severely limited.
Luckily, JavaScript creates an array of arguments which is accessible within a function; this array will contain any arguments passed to the function in the current call. This allows us to pass all the arguments to the aspect quite easily, opening up a lot of possibilities: we can log the arguments, take actions based on the arguments, and so on.
Unluckily, this vital item is probably the flakiest part of the entire script. I’m using the apply method, which allows us to invoke a function with an array of arguments. This function appears to be deprecated, which means that it may or may not be around in future. The real problem though, is with the scope. You see, the first parameter of the apply function defines the scope of the call – it tells the function what the “this” keyword will refer to. As it is, the scope is fudged, and may not refer to what you expected in many circumstances. This is the main reason for the great big warning above.
So…
While this was just a simple excercise to pass the time (and is by no means complete), everyone is free to tinker the results. If anyone actually manages to make something useful out of it, do let me know! đŸ˜‰
Interesting, but please never say again that in JavaScript 3rd Edition, the arguments object is an array đŸ˜€
Thanks for pointing it out, Andrea. What I should have said is that the arguments passed can be read from the arguments object *like* reading an array. Thanks for commenting đŸ™‚
Hello,
I’m glad you posted this document.
Please, consider this for your scope problem.
You can delegate the scope 2 times, you can operate in also in both aspect and object so the implementation is really at our reach.
Keep in touch, nice work.
Thank you for your comment and the tip, AndrĂ©s đŸ™‚ Will try it as soon as I have a minute!
I was looking for answers regarding this matter.
What i found is in javascript we can’t have programming tools at language level to define aspect, joint, crosses.
But by definition they are implementable, what i found is that you need a second pass to define those points, in a second pass you can pin any method.
Still events in most frameworks are handled by functions, with delegated scope this means that the concept of Event Handler is also missed, but we still work with events in more or less clear way.
I found that to create joints you can also add this feature as Observable adds the event handling capabilities, but remember adding this features will add weight to the already existent classes and implementation could be less indicated than the performance gain in the design.
cheers.
Oh, definitely – in practice, this is the kind of thing I’d want to add while I’m testing, rather than something I’d use in production, especially if the chain of aspects is larger than one or two functions. Even where performance is not an issue, Javascript suffers a lot from it’s flexibility when you’re working at scale, and these things make it VERY difficult to keep track of what’s affecting where – it can quickly become hell to maintain.
“I found that to create joints you can also add this feature as Observable adds the event handling capabilities” – Could you explain this further please?
Cheers! đŸ™‚
Of course, take the example of myObservable.addListener, this method is added to the Object class in the proto chain.
This way framework add event listening to all other classes.
In order to add aspect tools you can define Aspect classes and add joint tools to the base classes, got the idea?
The best would be myObject.myMethod.createJoint(cfg), perhaps you know how to use reflection here to get the method name, if it so then the task is done.
FYI, The .apply() method is not deprecated. In fact, it’s getting an enhancement in the next version of JavaScript. It will be able to take array-like objects in the future, instead of only taking arrays. A welcome change, IMO.
Perhaps you were confused because arguments.callee and arguments.caller got deprecated?
Thanks for the information, Eric! Yes – it’s possible, though I’m not sure… been some time since I wrote this đŸ˜›