Deprecated: Assigning the return value of new by reference is deprecated in /home/edelabar/ericdelabar.com/wp-settings.php on line 472

Deprecated: Assigning the return value of new by reference is deprecated in /home/edelabar/ericdelabar.com/wp-settings.php on line 487

Deprecated: Assigning the return value of new by reference is deprecated in /home/edelabar/ericdelabar.com/wp-settings.php on line 494

Deprecated: Assigning the return value of new by reference is deprecated in /home/edelabar/ericdelabar.com/wp-settings.php on line 530

Strict Standards: call_user_func_array() expects parameter 1 to be a valid callback, non-static method GoogleSitemapGeneratorLoader::Enable() should not be called statically in /home/edelabar/ericdelabar.com/wp-includes/plugin.php on line 311
Duck Punching JavaScript - Metaprogramming with Prototype at Eric DeLabar

Duck Punching JavaScript - Metaprogramming with Prototype

First, let me explain the title for those of you who live in Java world. The term Duck Punch comes from Ruby programmers and refers to the concept of duck typing used by Ruby and other languages; if it walks like a duck and quacks like a duck, I would call it a duck. With that being said, duck punching is summed up nicely by Patrick Ewing with the following:

Well, I was just totally sold by Adam, the idea being that if it walks like a duck and talks like a duck, it’s a duck, right? So if this duck is not giving you the noise that you want, you’ve got to just punch that duck until it returns what you expect.

It is slightly violent, but makes for a good article title. Now, getting to the point, metaprogramming is writing programs that write or manipulate programs, including themselves, at runtime. This type of programming can be accomplished in many languages but is currently a pretty hot topic in languages such as Ruby and Groovy. Java has a similar concept known as Aspect Oriented Programming (AOP), which introduces pointcuts to a program that can then be modified at runtime via bytecode manipulation or dynamic method proxies.

My example library/method below allows a Prototype-enabled class to have every one of its methods wrapped with a before and/or after method to introduce additional functionality at runtime. This concept is demonstrated by this example, note that the modification occurs on the class level and any existing or future instances of that class are affected. Thanks to Prototype JS, this code is pretty simple:

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
var Aspect = Class.create();
Aspect.prototype = {
	initialize: function( clazz, before, after ) {
		var members = Object.keys( clazz.prototype );
		members.each(
			function( name ) {
				if( Object.isFunction( clazz.prototype[name] ) ) {
					var oldName = "old_" + name;
					clazz.prototype[oldName] = clazz.prototype[name];
					clazz.prototype[name] = function() {
						if ( before ) before( name );
						var me = this;
						var args = $A( arguments );
						args.each( 
							function( arg ) {
								me[oldName] = me[oldName].curry( arg );
							}
						);
						me[oldName]();
						if( after ) after( name );
					}
				}
			}
		);
	}
}

This code relies on one of the core concepts of JavaScript OOP; a JavaScript object is nothing more than an associative array. Knowing this, when passed a class, we can get a list of all class member names using the Prototype Object.keys method as shown on line 4. Once we have the member names, we loop through all members (line 5) of the class. For each member we check if it is a function (line 7). If it is, we alias the function (line 9) and create a new function (line 10) in its place that wraps the aliased function with calls to the before (line 11) and after (line 20) functions we pass in. Notice the use of clazz.prototype starting on line 9, the prototype keyword is necessary to modify the class. If you rewrote this method slightly and didn’t specify the prototype keyword you could modify an individual instance of the class instead of all running instances. Also notice the curry method on line 16, currying is a metaprogramming technique (provided as a helper by Prototype JS) where a function is modified with each call so that the arguments passed into it become part of the function and are no longer necessary. This allows us to pass an arbitrary number of arguments into the aliased method. (Remember, in JavaScript, arguments do not need to be defined for a function and can be accessed via the arguments array.)

The above code can be called on any class in the document by running the following code, assuming before and after are pre-existing functions and SampleClass is the desired class:

1
new Aspect( SampleClass, before, after );

You can see a complete (although pretty useless) example of this method here, or view the source code for the test or the sample class.

So, where is this useful? Since we’re simulating AOP, we can use this for any cross-cutting concern, a perfect example being logging or profiling. Watch for a future article where I use this technique to automatically implement JavaScript profiling similar to what I did in my article on the dollar function using this technique. One final note for the Prototype geniuses out there, Prototype does provide a wrap method that provides this functionality, but if you used that you never would have learned all the neat things about JavaScript in this example!

One Response to “Duck Punching JavaScript - Metaprogramming with Prototype”

  1. kangax:

    Haha, I was just about to mention #wrap : )
    #wrap does indeed make wonders - I made a little write-up about it: http://thinkweb2.com/projects/prototype/wrap-it-up/ and played with “AOP” simulation: http://github.com/kangax/protolicious/tree/master/class.addbehavior.js

    May 2, 2008 6:33 pm

Trackback URI | Comments RSS

Leave a Reply