JavaScript closures act like implicit function state?
Consider this JavaScript code:
function acc( n ) {
return function( i ) {
return n += i;
};
}
var fn = acc( 5 );
var n1 = fn( 1 );
var n2 = fn( 2 );
The question, of course, is what the values of n1
and n2
will be. It is perhaps evident that n1
must now equal to 6
. But what about n2
? Will it now contain 7
(i.e. 2 + 5
) or will successive calls to fn
result in the parameters being accumulated by addition with the value that was passed to acc
(5
)? We find by experimentation that the latter turns out to be true. n2
now equals 8
, i.e., 5 + 1 + 2
!
The conclusion to draw here therefore is that JavaScript function closures are essentially implicit function state. In the code snippet given above, if you treat fn
as a regular object (which in fact it is), then the variable n
which is part of the closure captured by the function object when it was returned from acc
now acts like member state of fn
. This is why multiple calls to fn
causes the mutation to n
to persist across those calls.
This is further corroborated by the fact that a subsequent call to acc
to create another function object results in that instance getting a separate copy of the closure containing n
. Here's an example:
function acc( n ) {
return function( i ) {
return n += i;
};
}
var fn = acc( 5 );
var n1 = fn( 1 );
var n2 = fn( 2 );
var fn2 = acc( 10 );
var n3 = fn2( 1 );
var n4 = fn2( 2 );
Here, n3
and n4
hold 11
and 13
respectively. The function fn
has no effect whatsoever on fn2
(or vice versa). This in fact, is the basis for creating the C++ equivalent of private member data in JavaScript. Imagine that you wish to create the equivalent of the .NET StringBuilder class and want to make the buffer where the string is actually stored a private member of the class. If you did this:
function StringBuilder() {
this.buffer = [];
}
Then buffer
is a public member and can be accessed via an instance. To make it private, simply declare buffer
as a local variable inside StringBuilder
. Like this:
function StringBuilder() {
var buffer = [];
this.getBuffer = function() {
//
// TODO:
// return a copy so the original buffer is
// left intact
//
return buffer;
}
}
Now buffer
is not visible to routines defined outside StringBuilder
via an instance. But all methods defined inside StringBuilder
can access buffer
like any other member. You'd have to add accessor methods if you wished to provide access to private data. The same principle applies to member methods as well. Any local functions that you defined inside StringBuilder
remain accessible only from other functions defined inside that class.
Cool eh?!