Closures and Namespaces
This reading is supplemental to Chapter 8 (through page 173), which uses Immediately Invoked Function Expressions (IIFEs) to establish namespaces. This reading also discusses closures, which are briefly described on page 133.
A closure is a special kind of function, and a namespace is a way that a programming language can support separation of code. Consequently, you'll learn more about these concepts in CS 251 (Programming Languages), but they are important, practical concepts that arise naturally in JavaScript code, so we'll learn about them now.
But, before we get to closures, let's review some ideas that you probably remember from prior CS classes, but I want to bring to the top of your mind.
Scope¶
In JavaScript, variables can be local or global. A global variable can be seen by any code anywhere; local variables can only be seen from a small part of the code. For example, consider the following code:
var glob = 13;
function fred(x) {
return x+glob;
}
function george(x) {
x = Math.random();
glob = Math.random();
return x+glob;
}
The global variable glob
is visible to both functions. (The function
names fred
and george
also go into the global namespace and so are
visible to each other. For example, they could invoke each other.)
The local variable x
is local to each function. I could
simultaneously
invoke fred and george with different values for x
and nothing would go wrong, because they are different names and therefore
different storage locations. Imagine each function is a family, and both
families have a child named x
(short for Xenophon
and
Xerxes
). Different kids, different storage locations, but the
identical names cause no confusion. For the functions, the x
variable
exists only during the function call and disappears when the function
completes.
Other Scopes¶
There are other scopes in addition to local and global. We actually used these in the Plotting assignment without realizing it. Here's an example; see if you can figure out what it does:
function curve13(max, incr) {
var xvals = range(max);
var f = function (x) { return x+incr };
var yvals = xvals.map(f);
newPlot(xvals, yvals)
}
Focus on the f
function. What is the scope of x
? It's local to f
,
of course. What is the scope of incr
? The incr
variable is local to
curve13
but it is non-local to f
. The incr
variable refers to the
environment that curve13
created.
A variable that is non-local like incr
means that f
is a
closure. We say that f
is a "closure over incr
".
If a closure is returned from a function, it can continue to refer to
its original environment. We did this in the Plotting assignment as well:
the functions returned from quadratic
and cubic
continued to refer to
their original environments, which is how they continued to know their
coefficient and roots.
What is a Closure?¶
Formally, a closure is a function plus an environment (you can read more about them in the Wikipedia article on Closures), so to begin getting a handle on them, we start with some more observations about functions.
Functional Programming¶
The following functions f1
and f2
depend only on their inputs (rather
than on some global state information), which makes them easier to
understand than the function g1
which depends on some global variable
glob
. (For example, suppose glob
is a string: then the value of g1
is a string, too.)
var f1 = function (x y) { return x + y; };
var f2 = function (x) { return x + 4; };
var g1 = function (x) { return x + glob; };
When we talk about ''functional programming'' in Computer Science, we are
thinking about functions like f1
and f2
, where calling the functions
with the same inputs always yields the same output. The same can't be said
of g1
. In general, when we say some code is functional, we mean that
it depends only on its arguments, not on external state (like glob
) or
even internal state (its prior history of executions). We'll return to
this later.
Function f1
is just the add
function; we might call f2
the add4
function. Since functions are first-class objects in JavaScript, we can
dynamically create them and return them from functions. So, the following
function can create functions like add4
:
var makeAdder = function (delta) {
return function (x) { return x+delta; }
};
var add4 = makeAdder(4);
alert(add4(3));
Now, what kind of function is add4
? It's functional, in the sense that
its value only depends on its arguments, not on any global state
(certainly none that can change). Yet the code looks nearly the same as
g1
. What's the difference? The difference is that the glob
that g1
refers to is global and can change dynamically, while delta
is a
lexical variable in the environment of the anonymous function returned
by makeAdder
. That function includes both the function plus that bit of
environment, so it is a closure.
If you think about our earlier discussion of scopes and environments, the
environment of the anonymous function returned by makeAdder
includes the
parameter delta
and the delta
continues to exist even though
makeAdder
has returned. It continues to exist because the anonymous
function needs it. We say that the anonymous function has closed over
the name delta
.
An Example: A Shopping Example¶
Let's see closures in action in a web page. Suppose we have a page that has an item for sale, say apples, and it has an associated button that increments a counter, updates a database, updates the web page and maybe other things. So, you write some code like this:
function updateAppleDatabase(n) {
console.log("Apple Database updated with "+n);
}
Here's the HTML code:
And here's the JavaScript code:
The event handler function we've written is not functional (it depends
on the global variable appleClicks
). Nevertheless, the code works fine.
Next, we decide to add additional buttons for bananas, coconuts, dates, eggplants, figs and the rest of the alphabet. Of course, we could copy/paste the code above for each of our grocery items, but copy/paste usually a bad idea. Duplicate code means that if we change our implementation, we have to update all the copies of the original implementation.
Event Handler Maker¶
But how do we write a generic
event handler? We could write a
function to make one. While we're at it, we'll make the
updateAppleDatabase
function more generic.
function groceryClickHandlerMaker(item,outputId) {
var counter = 0;
return function () {
counter++;
updateDatabase(item,counter);
$(outputId).html(counter);
};
}
How do we use the groceryClickHandlerMaker
? Here are two examples, where we
invoke the groceryClickHandlerMaker
function to return a closure that
we then attach as the click handler.
Here's the JavaScript code:
Closure Variables¶
Let's look again at the groceryClickHandlerMaker
:
function groceryClickHandlerMaker(item,outputId) {
var counter = 0;
return function () {
counter++;
updateDatabase(item,counter);
$(outputId).html(counter);
};
}
What variables does it close over? It closes over every variable that is not defined within the function. Here there are three:
item
outputId
counter
The first two closure variables won't change ever, so the JavaScript engine can really just substitute the values as static constants. The counter, however, is interesting, because it's updated by the closure invocation, which means that the closure isn't functional in the technical sense of its behavior only depending on its arguments. Its behavior depends on how many times its been executed before.
Namespaces¶
Some very cool properties of the counter
variable used by the click
handlers above are that
- there are several of these variables, one for each handler
- they all have the same name, but
- they are all completely unrelated, and
- they are all completely private
Think about that last property for a moment: each click handler has its own private state variable. (This should remind you of instance variables in object-oriented programming.) Indeed, each closure has its own private namespace.
What's a namespace? In general, a namespace is a mapping from names (symbols) to values. You can think of the names as variables, as we will here, since in JavaScript we can assign a function to a variable. Namespaces are important in programming because they help us avoid name collisions. For example, many years ago, I was doing some graphics programming and I has some variables that stored the hex codes for various colors:
var red = 0xff0000;
var white = 0xffffff;
var tan = 0xd2b48c;
var teal = 0x00ffff;
...
I also needed to compute the tangent of an angle:
var tan_theta = tan(theta);
I'm embarrassed to admit that it took me hours to understand why the
tangent function wasn't working. The problem was a name collision: I had
only one namespace, so tan
could only have one value.
In JavaScript, the tan
function is safely in the Math
object and so we
say Math.tan
to use it. The Math
object gives us a kind of namespace.
We could create one for Colors very easily in JavaScript using objects:
var Colors = {};
Colors.red = 0xff0000;
Colors.white = 0xffffff;
Colors.tan = 0xd2b48c;
Colors.teal = 0x00ffff;
We can put functions in the object as well, like the tan
function is in
Math
. Suppose we had a function lighten
and we didn't want to clutter
up the global namespace with it. We could put it in Colors
:
Colors.lighten = function (color) { ... };
This technique is very common in JavaScript programming. Another, even more common, is to use functions.
Functions as Namespaces¶
Let's take a very abstract example so that the details of the code don't
confuse the issue. Suppose we want to have a bunch of global variables
and functions with short, succinct names for brevity. The functions might
be mutually recursive, might refer to the globals, and so forth. After all
those definitions, the thing we actually want to do is compute f(1)
and
insert the result into page . Our code might look like this:
var a = 123;
var b = 456;
var f = function (n) { ... g(a*n); ... };
var g = function (x,y) { ... f(b*x)+f(b*y)...; }
$("#ans").html(f(1));
This code would work, but it would not be good software engineering, because of the potential for namespace conflicts. So, we can just make all these variables be local variables of a new, anonymous function that we will immediately execute.
(function () {
// code from above
})();
Notice that the function expression is wrapped in parentheses: that's necessary so that it won't be treated as just a top-level function definition. The parenthesized function expression is then followed by a pair of empty parentheses which causes the function to be invoked. This is called an Immediately Invoked Function Expression or IIFE. Wikipedia has more about Immediately Invoked Function Expressions.
Using an IIFE means that no names are added to the global namespace, so
the code has no footprint at all. (The footprint
of code is the set
of names that are added to the global JavaScript namespace.)