LEVEL: INTERMEDIATE
PREREQS: OBJECTS
You've seen it before. You're setting up an XMLHttpRequest call, and you need to execute a function when it returns, so you do something like this:
Expand|Select|Wrap|Line Numbers
- http.onreadystatechange = myAwesomeFunction;
You read somewhere that you can do this:
Expand|Select|Wrap|Line Numbers
- var width = 100, height = 300;
- http.onreadystatechange = function() {
- myAwesomeFunction();
- doSomethingAmazing(width, height);
- };
Well, here's why:
All functions in JavaScript are actually objects.
That's right. A function is an object, just like window, or document, or 2, or 'Hello, World!', or....
If you're familiar with JavaScript objects, you probably know that scalars are objects. For example:
Expand|Select|Wrap|Line Numbers
- var myNumber = 2;
- var myNumber = new Number(2);
- var myString = 'Hello!';
- var myString = new String('Hello!');
- var myBoolean = true;
- var myBoolean = new Boolean(true);
So if numbers and booleans and other types that we take for granted are all objects, why not functions?
Expand|Select|Wrap|Line Numbers
- function doSomething(x, y) {
- return x * y;
- }
- var doSomething = new Function('x, y', 'return x * y;');
Ok, so functions are objects. So what?
Well, let's go back to the XMLHttpRequest snippet above:
Expand|Select|Wrap|Line Numbers
- http.onreadystatechange = myAwesomeFunction;
Similarly to how you could assign myVar a value of 2 like this:
Expand|Select|Wrap|Line Numbers
- var someObject = new Number(2);
- myVar = someObject;
Expand|Select|Wrap|Line Numbers
- var someObject = new Function('', 'alert("Hi!");');
- // Or...
- function someObject() {
- alert("Hi!");
- }
- http.onreadystatechange = someObject;
Remember how you were able to store multiple functions to http.onreadystatechange by doing this:
Expand|Select|Wrap|Line Numbers
- var width = 100, height = 300;
- http.onreadystatechange = function() {
- myAwesomeFunction();
- doSomethingAmazing(width, height);
- };
Remember how we created a function using the Function constructor:
Expand|Select|Wrap|Line Numbers
- function callSomeFuncs() {
- myAwesomeFunction(response);
- doSomethingAmazing(width, height);
- }
- // Which is equivalent to this:
- var callSomeFuncs = new Function('', 'myAwesomeFunction(response);\ndoSomethingAmazing(width, height);');
- // In both cases, we can assign http.onreadystatechange the 'value' (function object) of doSomething like this:
- http.onreadystatechange = doSomething;
Expand|Select|Wrap|Line Numbers
- http.onreadystatechange = new Function('', 'myAwesomeFunction(response);\ndoSomethingAmazing(width, height);');
- // Remember that these work:
- var myNumber = new Number(2);
- var myString = new String('Hello!');
- var myBoolean = new Boolean(true);
Expand|Select|Wrap|Line Numbers
- http.onreadystatechange = function() {
- myAwesomeFunction(response);
- doSomethingAmazing(width, height);
- }
Incidentally, this is an example of what we like to call an 'anonymous function', since we're not defining it with a variable name to the right side of the function keyword.
But that doesn't mean that our function disappears!
Expand|Select|Wrap|Line Numbers
- http.onreadystatechange = function() {
- alert('Hi!');
- }
- http.onreadystatechange();
One more:
Expand|Select|Wrap|Line Numbers
- var myFoo = function() {
- alert('Hi!');
- }
- var someBar = myFoo;
- someBar();
- // Why does this work? Well, for the same reason that this works, but instead of calling the function stored in someBar, this code passes the value stored in someBar (2) to the function stored in document.write.
- var myFoo = new Number(2);
- var someBar = myFoo;
- document.write(someBar); // Outputs '2'.
For more information, take a look at the MDC JavaScript Reference
*[SIDEBAR: Actually, the second statement will execute a tiny fraction of a second more slowly because explicitly-defined functions (using the 'Function' constructor) are interpreted rather than compiled. So you should try to use the function keyword rather than explicitly creating Function objects wherever possible.]