Performing frequent training courses has given SitePen a chance to learn new ways to improve our training approach. Some concepts in JavaScript can be trickier than others and as we interact with the classes, we’re able to develop fun explanations for these tricky concepts. I’d like to share one of the explanations I developed at a recent training course.
A common phrase that results from these training sessions is “I didn’t know JavaScript could do that.” The first time we hear it is usually after we’ve covered how to work with functions in JavaScript, when we start showing how JavaScript allows us to use functions (in an approach termed functional programming). We try to conclude each part of the course with code examples that incorporate everything from the section we’ve just covered. At the end of our section on functional programming, we have the following example:
addTo = function(a, log){
return function(b){
if(log){
log(a, b);
}
return a+b;
}
}
var addToTen = addTo(10, function(x, y){ console.debug(x, y); });
addToTen(5) == 15; // Logs: 10, 5
Note: the console object is part of the console API which is part of Firebug and an increasing number of browser debugging tools.
Those coming from a less dynamic language can have a hard time following the series of events that result in that log statement being printed out. The order in which function objects are created or executed is determined by how they’re defined and used, and often out of order in terms of line numbers. The origins of each of the used variables can likewise be hard to trace.
We might not even realize that our brain is trained to think in a certain manner. After two years of experience in a Java shop, when I looked at my code, I was subconsciously figuring out how my code all worked together. Pause for a second, open up some source code in a language you’re familiar with, and consider how much you subconsciously parse simply by looking at the code. Unless you’re coming from a language where the code you write reads from top-to-bottom with no other code executing in between, you should realize that your mind is jumping in and out of functions, in between classes and files.
One of the most important things to understand with JavaScript is that the functions we create are all totally dynamic and all of them exist as real objects. Unlike languages that use a compiler to turn functions and classes into a set of static routines, function objects do not exist until the JavaScript parser has reached that line in the file. This is contradictory to both compiled languages (e.g. Java) where functions act as either class or instance methods and are referenced with static locations, or languages where functions are ever-present (e.g. PHP) and you can call them even before the runtime parser has reached their definition (JavaScript itself is one of these languages if you give your function a name).
Notice that when I talked about functions in JavaScript above, “functions” were called “function objects”. That they are objects is a key idea of JavaScript and functional languages in general. By functions becoming objects, instead of some functionality hidden behind a compiler and a variable name, they become a “first-class” citizen of the language.
As a way of distancing these old ways of thinking even further, let’s start calling these function objects “subroutine objects”. When the browser (or any other JavaScript runtime) encounters one of these definitions, it says “I am not supposed to run this right now, I’ll have to save this code in memory somewhere, so that it can be deferred until it’s needed”. When this happens, the way I think of it is that the runtime deletes all of the code within the function, creates an object, assigns the removed code to this object, and sticks this new object where the code used to be.
Being taken out of the main routine makes it a subroutine, and the way that this subroutine is stored in memory is through an object that holds not only that code, but some other goodies as well which we’ll cover in detail by the end of this post.
Take a look at this next code block. The way that the entire function is blocked off is an important thing to remember. Blocked out code is completely removed from the main routine and replaced with a subroutine object:
In the last SitePen training course, while explaining the way that the various components in this example all ended up working together, there was still some confusion about exactly what these subroutine objects contained. I told you that the subroutine object held onto the code that it removed, and there’s a way in JavaScript to examine it: the toString
method. As we look at how this code example works, we’ll be able to use the toString
method at each step.
In order to show the series of events, the various components are numbered in the order that they’re used (in grey) or defined (in blue):
Code in Item 1 is removed from the main routine and stored in a subroutine object that is assigned to the global addTo
variable. None of the code in Item 1 is altered in any way when this subroutine object is created. Remembering that we’re thinking of these pieces as objects that are encapsulated subroutines, this means we can view exactly what code is encapsulated in Item 1 by using the toString
method.
console.debug(addTo.toString());
gives us the string:
function (a, log) {
return function(b){
if(log){
log(a, b);
}
return a + b;
}
}
We can see the definition for Item 4 within this subroutine object. But that’s all it is, a definition. And until Item 1 is executed, Item 4 will remain a definition.
The code following the creation of the addTo
subroutine object will execute the addTo
subroutine. But before the code in the subroutine object gets executed, any expressions in the passed arguments must be resolved. In the second argument, we see Item 2‘s subroutine object being created. Once this subroutine object is created, it’s passed as part of the execution of the addTo
subroutine. If we add a toString
to the top line of the addTo
function, we can see that the subroutine is an object, that its contents haven’t been executed yet, and what code it contains:
console.debug(log.toString());
gives us the string:
function (x, y) { console.log(x, y); }
Each time the Item 1 subroutine is called, it gets a new set of local variables, what we call a new execution scope. This means that the values a
and log
that are passed to it are local to the code within the subroutine. These variables won’t leak out into the rest of the program.
Now that the subroutine in Item 1 is being executed, it’s part of the main program routine. This means that any function definitions it encounters will once again be removed from the routine and stored in subroutine objects. As the code in Item 1 executes, this new subroutine object is created (Item 4) and returned. As mentioned earlier, each time a subroutine object’s code is executed, it gets a new execution scope. This means that each time you call the subroutine in Item 1 it removes the code at Item 4 and assigns it to a totally new subroutine object.
By assigning the returned subroutine object to the local variable addToTen
, we can use the toString
method once again to see what code it contains. As this subroutine object is being returned from a function call, seeing what code it contains is particularly illuminating.
console.debug(addToTen.toString());
gives us the string:
function (b) {
if (log) {
log(a, b);
}
return a + b;
}
This string resembles the exact code contained within Item 4, removed perfectly! But do you notice anything odd about the code that this subroutine contains? It has references to local variables that aren’t passed to the function: log
and a
(you can see that b
will be passed as an argument to the code in the subroutine).
Where do these come from? It was mentioned earlier that these subroutine objects hold more than just code. Subroutine objects in JavaScript are objects referred to in computer science as closures. A closure is basically a fancy way of saying that a function remembers what variables were around it when it was declared. The other subroutine objects that we’ve worked with so far haven’t had to remember variables, because all of the variables they reference exist within the function as local variables.
The way that these variables are remembered is completely invisible to us, there is no toString
equivalent. Each individual variable isn’t encapsulated by the subroutine object, the scope that the subroutine object was created in is. What this means is that when the subroutine code is finally executed, it first looks at its own inner scope to resolve variables, and if a variable is not found there, it looks in its containing scope (held on to by the subroutine object). This can confuse some programmers who think that each variable is encapsulated individually.
One of the ways our brain can trick us here is to think that when JavaScript removes the code and replaces it with a subroutine object, it replaces all of the variables with their current values. Instead, the removed code is not altered in any way.
Finally, the function call we make in Item 5 runs the code in Item 4, which is holding on to the encapsulated scope (created when Item 1 was executed) containing a
and log
(which we passed as part of the call in Item 3)
The only thing we pass to addToTen
is the number 5
, which will be accepted as the local variable b
in the subroutine code. During the call, the code contained in the log
subroutine object is executed, a
and b
are added together and returned.
Why It Matters
A student said it best when he said that being able to do this sort of programming is like “writing a function template”. The power in this example is that we’re able to create a function, we’re able to define a template (Item 4), and we’re able to define the variables by leaving them out of our template, but defining them in the execution scope the subroutine object gets created in.
By doing this, JavaScript “fills in the blanks”, effectively giving us the variables a
and log
for use in our template in Item 4
. We can execute the addTo
subroutine as many times as we want, each time getting a new subroutine object that behaves the same way, but with variability.
Moving Forward
We cover this material as part of our training so that when we start to get involved with the Dojo APIs, the students are comfortable and familiar with the power and flexibility of a system that uses callbacks (what we call a subroutine object that is executed by another function).
Many of our API calls, from dojo.xhrGet, to functional array iteration, to the Dojo Data API’s fetch method, accept these subroutine objects and will execute them when an Ajax call returns, for each item in an array, or when some data is loaded.
Knowing that the language works this way also helps prevent bugs as students are getting familiar with developing in JavaScript. If you haven’t been taught what these subroutine objects are, and how they end up being able to “see” the execution scope they were created in, you can easily end up writing code that will cause headaches later. Being aware of how JavaScript manages functions isn’t just theoretical, it allows you to create more powerful code, and avoid writing code with harmful side-effects.