Continuing the series on JavaScript for C# developers, in this episode we look at closure.
First the definition. A closure is a function that encloses its local environment when it is created.
Now, in C#, the function talked about in the definition above is a lambda expression or an anonymous method. The language definition for C# talks about the outer variables of an anonymous method, these are the local variables and parameters that are in the same scope as the anonymous method (note that this
is such a variable). The anonymous method is said to capture a local variable if its code makes reference to that variable. The variable, if it is captured, may have a lifetime that is longer than you'd expect from the outer enclosing scope. The function is a closure that captures these local variables.
That all sounds terribly dry, so let's take a look at an example. We'll create a counter function.
// definition of a method that returns an int delegate int Counter(); // method that creates a counter function static Counter CreateCounter(int startValue) { return () => startValue++; }
The first statement defines a delegate that returns an int
. The CreateCounter()
method creates such a delegate by using a lambda expression. The expression returns the current value of startValue
, and post-increments it. The CreateCounter()
method then returns this lambda expression. If you're puzzled by the lambda expression, here's the equivalent using a "proper" anonymous method:
// method that creates a counter function static Counter CreateCounter(int startValue) { return delegate { return startValue++; }; }
The important thing to remember here is that startValue
, because it has been captured by the anonymous method, does not get destroyed when the CreateCounter()
method terminates, as would usually happen. The anonymous method has captured the parameter, and so it lives on.
Let's use it now:
Counter counter = CreateCounter(42); Console.WriteLine(counter()); Console.WriteLine(counter()); Console.WriteLine(counter());
And, when run, as you'd expect this produces:
42 43 44
So a closure in C# is nothing more than an anonymous method or lambda expression that captures one or more outer local variables or parameters.
And it's the same in JavaScript. Well, apart from the different rules of scope, that is. In fact, closures are the way you implement private variables for objects in JavaScript, but we'll get to that later. First of all, since you're familiar with the C# version of the counter, let's see how it looks in JavaScript. First, there's no point in defining the delegate type, so we'll get rid of that. Here's the createCounter
function:
var createCounter = function(startValue) { return function() { return startValue++ }; };
So we're defining a variable called createCounter
. This is a function taking a single parameter, startValue
, and that returns another function. This second function returns the current value of startValue
and post-increments it. Notice that, under the scope rules of JavaScript, the inner function has access to the startValue
parameter from the outer function, so this works as you'd expect. (And funnily enough it's very similar to the anonymous delegate version above.)
The code that executes this is virtually the same, and produces the same output in Firebug:
var counter = createCounter(42); console.log(counter()); console.log(counter()); console.log(counter());
In other words the inner anonymous function captured the parameter from the outer function and thereby formed a closure.
Let's go a little further now and create a point
object, one that has two properties, X and Y. If we were doing this naively we'd write this
var point = {X: 0, Y: 0};
Notice that we have full access to the internal fields of the object. We'd like to change that so that we can only read/write these values through property accessors. How is this done?
The answer involves a closure. Before looking at this answer, think about how you'd implement it for a moment: we want to declare a function (let's call it createPoint
) that returns an object with four methods: getX
, setX
, getY
, setY
. These methods will do what you'd expect: they're getters and setters for some internal private fields.
Let's build it up, step by step.
var createPoint = function() { return { getX: function() { }, setX: function(value) { }, getY: function() { }, setY: function(value) { } }; };
This is the bare minimum. The return statement creates a new anonymous object with the four methods, and these four methods do absolutely nothing at the moment. But note that the setters at least take a value
parameter whereas the getters do not.
What we'd like to happen is that there's a private field called x
and the getter for the X coordinate returns the value of that field, and the setter sets it. Ditto for the Y coordinate. Without worrying about the "private" bit for now, since we don't know how to implement it (there's no private
keyword), let's complete the createPoint
function in the most obvious — that is, C#-ish — way:
var createPoint = function() { return { x: 0, y: 0, getX: function() { return x; }, setX: function(value) { x = value; }, getY: function() { return y; }, setY: function(value) { y = value; } }; };
This returns an object with two fields and four methods. Unfortunately the fields are publicly visible. I can write this:
var point = createPoint(); point.x = 1; point.y = 2;
Even worse, the methods are broken. As they stand the x
and y
they refer to are not the variables in the object, but global variables. (Remember: JavaScript scope is by function, not by block or by object. The getX
method above doesn't have an x
local variable, so JavaScript goes and takes a look at the outer function. This doesn't have an x
function either. Since there is no outer outer function, x
is assumed to be a global object and is logically prefixed with "window.
". But window
doesn't have an x
variable either, and so getX()
will fail.) To fix the methods, I'd have to prefix the references to the variables with this
, something I haven't discussed yet. All in all, not a good solution.
However, it's pretty close. If I changed it to this:
var createPoint = function() { var x = 0; var y = 0; return { getX: function() { return x; }, setX: function(value) { x = value; }, getY: function() { return y; }, setY: function(value) { y = value; } }; };
Everything would suddenly work, and both x
and y
are invisible to the outside world; they are private. Let's see why.
First of all, notice that x
and y
are local variables in the createPoint
function. That automatically means they're not visible outside the function (that's what a local variable means, after all), and also, being JavaScript, that they're visible to all other functions declared inside the function as well. We have four such functions, the getters and setters — it doesn't matter if they're declared in another object like these are: scope in JavaScript is by function, remember. So, for example, getX
will return the value of x
. Which x
? Well, the one it can find in the scope chain, which is the one declared in the outer function.
Each of these getters and setters are closures over the two local variables. Each closure shares the same captured local variables: if you set the X coordinate with setX
, you'll retrieve the same value with getX
. The code below tests this important point (pun intended). First of all it creates a new point
variable, sets both the X and Y coordinates, and then prints the value of the point variable, that is, the coordinate pair.
var point = createPoint(); point.setX(1); point.setY(2); console.log("Point = (" + point.getX() + "," + point.getY() + ")");
As you can see from this, providing you declare an object through calling a function, you can get private variables. Encapsulation, to give it its real name. And the usual way of creating an object by calling a function? Construct it; that is, use a constructor. Next time, then, we'll look at classes. Er, sorry, prototypes.
Now playing:
Roxy Music - 2HB
(from Roxy Music)
4 Responses
#1 Dew Drop - Weekend Edition - February 21-22, 2009 | Alvin Ashcraft's Morning Dew said...
21-Feb-09 8:54 PMPingback from Dew Drop - Weekend Edition - February 21-22, 2009 | Alvin Ashcraft's Morning Dew
#2 My Bad Attitude » JavaScript for C# programmers: closure basics said...
22-Feb-09 2:20 PMPingback from My Bad Attitude » JavaScript for C# programmers: closure basics
#3 Dew Drop - February 23, 2009 | Alvin Ashcraft's Morning Dew said...
23-Feb-09 6:24 AMPingback from Dew Drop - February 23, 2009 | Alvin Ashcraft's Morning Dew
#4 JavaScript for C# programmers: prototypes and privacy said...
27-Feb-09 10:17 PMContinuing my series about learning JavaScript when you're a C# programmer, using Firebug in Firebox as our testing ground. In this episode, overriding, privacy, and class models. Last time we saw how to create inheritance from JavaScript's constructors
Leave a response
Note: some MarkDown is allowed, but HTML is not. Expand to show what's available.
_emphasis_
**strong**
[text](url)
`IEnumerable`
* an item
1. an item
> Now is the time...
Preview of response