Saturday, July 02, 2005

AOP in JavaScript

It's really easy to implement various aspect-oriented programming idioms in JavaScript. This shouldn't come as a big surprise, perhaps because it's a reflective language, but probably even more so simply because it allows you to mutate everything.

Danne Lundqvist implemented a little library that introduces before, after, and around advice to methods. It's very simple: it just replaces the method slot of an object with a wrapper around the original method.

Another creative aspect-oriented library for JavaScript is Ben Nolan's Behaviours library. The "advice" in this case is simply the standard DHTML event handlers, but the interesting part is the join-point model: CSS selectors. Because a web page is represented roughly as a tree of DOM objects (a graph, actually, because of the back edges), there are all kinds of libraries for encoding the selection of sets of nodes. Because CSS offers a high-level syntax for representing these selectors, Ben's library allows you to specify join points using those selectors and automatically attach new behavior to all the selected nodes.

Noel noted the connection to AOP on the Untyping blog, and I've made some comments about the design of such an AOP facility on Lambda the Ultimate.

Update: Here's a prototype implementation of a variation on Ben's library. I implemented some of the suggestions I made on Lambda the Ultimate.

3 comments:

Nils-Hero said...

mark up links.

lugnut said...

I'm not sure if you're still interested in this project, but I thought this implementation might be improved by allowing a drop-in resolver (e.g., from MochiKit). Here's a suggested patch:

--- behavior.js.orig    2010-02-14 09:52:45.000000000 -0800
+++ behavior.js 2010-02-14 09:55:54.000000000 -0800
@@ -98,6 +98,8 @@
 var Behavior = {
     registry : new Array,
 
+    resolver : document.getElementsBySelector,
+
     register : function(sheet) {
         Behavior.registry.push(sheet);
     },
@@ -112,14 +114,14 @@
     },
 
     apply : function() {
-        for (var i = 0; i < Behavior.registry.length; i++) {
+        for (var i in Behavior.registry) {
             var sheet = Behavior.registry[i];
             for (var selector in sheet) {
-                var list = document.getElementsBySelector(selector);
+                var list = Behavior.resolver(selector);
                 if (!list) {
                     continue;
                 }
-                for (var j = 0; j < list.length; j++) {
+                for (var j in list) {
                     Behavior.registerEventHandlers(list[j], sheet[selector]);
                 }
             }


You could use it like this:

<script type="text/javascript" src="MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="behavior.js"></script>
<script type="text/javascript">
Behavior.resolver = findDocElements;
var rules = { /* use MochiKit-style selectors here */ };
Behavior.register(rules);
</script>

frontline plus said...

many thanks. I am currently doing a project in JavaScript and am very interested in what you put here on Danne Lundqvist's library and Ben Nolan's Behaviours library. many thanks