Functions as Variables, or "Here, Do This."

Before jumping into the details, one aspect of Javascript requires some explanation. Unlike Java or C#, functions are first class objects in Javascript. You can do everything with a function that you can do with an integer, including:
  • Assign it to a variable
  • Use it as a value in an array, or associative array
  • Pass it as a parameter
Here's a simple example.  Suppose you want a function that calculates either the maximum or the minimum of two numbers, depending on another parameter to choose.  You can code it like this: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
function applyMaxOrMin(a, b, fnName){
   if(fnName == 'max'){
      return Math.max(a,b);
   }else{
      return Math.min(a,b);
   }
}
console.debug(applyMaxOrMin(1, 2, 'max'));

But you can make it a line or two shorter, and more general, by passing a function like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
function applyTwoParameterFn(a, b, fn){
   return fn(a,b);
}
console.debug(applyTwoParameterFn(1, 2, Math.max));

Interestingly, you can substitute any function that takes two numbers here, not just Max or Min.

"Ah," you might say, "that's like passing a class in Java and using reflection."  Almost, but not quite.  Java is still strict about type checking, even when calling functions through reflection.  In our example, what if the function passed aren't compatible?  In the example above, what if you pass a function with three variables?  It's handled in The Javascript Way, i.e. sloppily.  If the number of parameters doesn't quite fit, extra ones are lobbed off or null's are added to the end.  Type conversion is applied like it should.

Note: There is no concept of overloading in Javascript!  That's a fundamental difference between it and strongly-typed object languages like Java.

One special function type used a lot in dojo is the callback.  Callbacks are functions that are supposed to be called when processing has ended.  They are especially useful for asynchronous operations like XHR.  You call an asynchronous function and say "call this function when you're done."  If you've used event classes in Java, it's the same concept but looser.

In Java you can define classes anonymously, on-the-fly, right in the middle of a method call.  You can do that in Javascript too.  Simply define the function in the parameter list and omit the name.  For example, instead of defining and passing a new function:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
function myTwoParameterFn(a, b) {
   return max(a, -b);
}
console.debug(myTwoParameterFn(1, 2, Math.max));

We can shorten it to:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
console.debug(myTwoParameterFn(
    1, 2,
    function(a,b){
        return max(a, -b)
    }
));

This makes from some pretty strange syntax, especially if the anonymous function is large.  But callbacks are often specified this way when calling dojo functions.