Reprint!
The original 5,000 paper copies are just about sold out, and channels keep asking for more! So I’m happy to announce Pragmatic Programmers just triggered a reprint of 3,000 more copies.
Thanks to everyone for grabbing the book, I hope you’re loving it!
No commentsSorry for the outage, folks
For the past 24h or so, this web site was not accessible, due to a principle of precaution when some potential security issues arose, in order to protect the visitors’ privacy.
Unfortunately, this coincided with a major office rush, hence the long fix.
Thanks for hanging in, and welcome back.
3 commentsJavaScript Nuggets #1
This post opens up a new series on The Bungee Blog, called “JavaScript Nuggets.” In this series, I shall attempt to share with you a few bits of code I have been using and refining over actual projects, bits that I find myself reusing all the time, which means I’m happy enough with them.
Today we’ll talk about two generic functions I use in most of my backoffice screens to consistently “ajaxify” checkbox lists (persisting the checked ones on-the-fly, without needing to submit anything) and deletion of list items (with proper, dynamic confirmation dialog boxes).
4 commentsHook up at The Ajax Experience 2008

I’m delighted to report I’ll once again speak at The Ajax Experience, in its unique 2008 edition, to be held in Boston from Sep 29 to Oct 1, 2008.
I’ll hold a session named “What’s Up With Prototype and script.aculo.us,” that will showcase all the latest stuff in both libraries.
It’s going to be exciting for sure, especially with RailsConf USA offering so little in the way of client-side stuff…
Last time was a blast, so I look forward very much to this! If you attend, be sure to come and say hi!
No commentsNeuron Workout Solutions #5
Welcome to this fifth installment of Neuron Workout Solutions™, a series that answers the questions and challenges at the end of many chapters in the book. Today we’ll address the challenges closing chapter 10: More Useful Helper Objects. And with this, we’ll wrap up the Neuron Workouts for the Prototype part of the book. The next installment will begin the challenges about script.aculo.us!
2 commentsNeuron Workout Solutions #4
Welcome to this fourth installment of Neuron Workout Solutions™, a series that answers the questions and challenges at the end of many chapters in the book. I know this one’s been a tad longer than the others in coming, so today we’ll address the challenges closing two chapters: chapters 8 (Form Management) and 9 (Ajax Has Never Been So Easy).
No commentsDoing one’s homework
In the mail this evening:
Hi Christophe,
I found your name through some links, where I started out using some javascript. Through a chain of events we have incorporated (to the best of our abilities) some coding that I believe started with an affiliate company you did work with “Prototype”.
run away!
Talk about not knowing what the hell you’re talking about.
No commentsNeuron Workout Solutions #2
It is high time for this second installment of Neuron Workout Solutions™, a series that answers the questions and challenges at the end of many chapters in the book. Today we’ll address the challenges closing chapter 5, which focuses on the wonderful Enumerable mixin.
Mixing argumentNames and wrap for varargs-like sweetness
Back in the first Neuron Workout solutions, I suggested an exercise for implementing varargs-like functionality using two of Prototype’s extensions to Function: argumentNames and wrap. I also promised I would post a possible solution here.
The idea was to provide a mechanism that would take an object or class in, and alter any method ending with a parameter named anythingElse so that such methods would end up with any “remaining” arguments stored as an array in this special parameter.
As promised, here’s the code for it:
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | Object.enableVarArgs = function(object) { for (var methodName in object) { var method = object[methodName]; if (!Object.isFunction(method)) continue; (function() { var argNames = method.argumentNames(); if (argNames.last() != 'anythingElse') return; object[methodName] = method.wrap(function() { var args = $A(arguments), proceed = args.shift(); args[argNames.length - 1] = args.slice(argNames.length - 1); args.length = argNames.length; return proceed.apply(this, args); }); })(); } }; Class.enableVarArgs = function(klass) { return Object.enableVarArgs(klass.prototype); }; |
Before diving too much into the inner workings of this, let’s put together a demo object, reuse it for a demo class, and see whether this all works:
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | var obj = { method1: function(a, b) { console.log("CALLED: method1(" + Object.inspect(a) + ', ' + Object.inspect(b) + ")"); }, someMethod: function(a, b, anythingElse) { console.log("CALLED: someMethod(" + Object.inspect(a) + ", " + Object.inspect(b) + ", " + Object.inspect(anythingElse) + ")"); }, method2: function(anythingElse) { console.log("CALLED: method2(" + Object.inspect(anythingElse) + ")"); } }; var Klass = Class.create(obj); var obj2 = new Klass(); Class.enableVarArgs(Klass); Object.enableVarArgs(obj); obj.method1(1, 2, 3, 4); obj.someMethod(1, 2, 3, 4); obj.method2(1, 2, 3, 4); obj2.method1(1, 2, 3, 4); obj2.someMethod(1, 2, 3, 4); obj2.method2(1, 2, 3, 4); |
I used Firebug’s console object here, but you may use interactive alert calls just as well. On my browser, the output goes like this:
CALLED: method1(1, 2) CALLED: someMethod(1, 2, [3, 4]) CALLED: method2([1, 2, 3, 4]) CALLED: method1(1, 2) CALLED: someMethod(1, 2, [3, 4]) CALLED: method2([1, 2, 3, 4])
Okay, so how does this work?
Wel, we start by iterating over the object’s methods. This can easily be done by using a regular for…in loop to grab all the properties and filter using Prototype’s Object.isFunction predicate (which is just a typeof test).
11 12 13 | for (var methodName in object) { var method = object[methodName]; if (!Object.isFunction(method)) continue; |
Now the major trick was to avoid scope/reference issues with the remainding variables, most importantly argNames.
Because of JavaScript scoping issues, argNames will usually be a shared reference for all iterations of that loop. For instance, consider our test object: two of its methods match our criteria: someMethod, with argument names a, b and anythingElse, and method2, with just anythingElse. Because of this, the method replacement code (the one used in our wrap call) for someMethod will end up using the value of argNames that was set by processing method2, resulting in a wrecked final arguments list.
The traditional way of coping with such situations is to give such variables a guaranteed private scope, which is achieved by creating an anonymous function for the scope and calling it right after it’s defined (a technique used for implementing the famous module pattern, too).
This is why we wrap the remainder of our code inside this anonymous function, which we then call immediately (hence the weird )() code fragment).
14 15 16 17 18 19 20 21 22 23 | (function() { var argNames = method.argumentNames(); if (argNames.last() != 'anythingElse') return; object[methodName] = method.wrap(function() { var args = $A(arguments), proceed = args.shift(); args[argNames.length - 1] = args.slice(argNames.length - 1); args.length = argNames.length; return proceed.apply(this, args); }); })(); |
So on to the code inside this anonymous function.
We start by filtering more on the last argument name, to check whether it is indeed anythingElse. As we’re not in the lexical scope of the surrounding for loop now, we can’t just call continue, we need to return from the anonymous function (since the loop has no code after this function, it’s pretty much equivalent).
15 16 | var argNames = method.argumentNames(); if (argNames.last() != 'anythingElse') return; |
Then we replace the former code for the method by our new code, which wraps the original one. Wrapped methods always received the former version as their first argument, which we traditionally call proceed. To easily tweak the arguments actually passed to our new methods, we grab the JavaScript predefined arguments variable, pass it to $A to make sure we have a proper, full-fledged Array to play with, and take the first argument out as our original method (proceed).
18 | var args = $A(arguments), proceed = args.shift(); |
All that is left to do is put all arguments from the anythingElse-positioned one to the end of the actual argument list in an array to be passed instead of anythingElse, and “cut” the arguments array to the proper length:
19 20 | args[argNames.length - 1] = args.slice(argNames.length - 1); args.length = argNames.length; |
The actual call is performed by the native apply method on our original function, which has the good sense of taking the arguments as an array (which makes it ideal for our purposes). Of course, if there was an original returned value, we need to propagate it by returning it too.
20 | return proceed.apply(this, args); |
Feel free to ask your questions in the comments, and discuss your own solutions if you had worked out any!
You can also download the full demo script for this example and play with it (you’ll need prototype.js loaded, obviously).
Also note I’m working on the second installment of the Neuron Workout Solutions, so stay tuned!
No comments