loading...

JavaScript – Closures

Like most modern programming languages, JavaScript uses
lexical scoping. This means that functions are
executed using the variable scope that was in effect when they were
defined, not the variable scope that is in effect when they are
invoked. In order to implement lexical scoping, the internal state of
a JavaScript function object must include not only the code of the
function but also a reference to the current scope chain. (Before
reading the rest of this section, you may want to review the material
on variable scope and the scope chain in Variable Scope and The Scope Chain.) This
combination of a function object and a scope (a set of variable
bindings) in which the function’s variables are resolved is called a
closure in the computer science
literature.[13]

Technically, all JavaScript functions are closures: they are
objects, and they have a scope chain associated with them. Most
functions are invoked using the same scope chain that was in effect
when the function was defined, and it doesn’t really matter that there
is a closure involved. Closures become interesting when they are
invoked under a different scope
chain than the one that was in effect when they were defined. This
happens most commonly when a nested function object is returned from
the function within which it was defined. There are a number of
powerful programming techniques that involve this kind of nested
function closures, and their use has become relatively common in
JavaScript programming. Closures may seem confusing when you first
encounter them, but it is important that you understand them well
enough to use them comfortably.

The first step to understanding closures is to review the
lexical scoping rules for nested functions. Consider the following
code (which is similar to code you’ve already seen in Variable Scope):

var scope = "global scope";          // A global variable
function checkscope() {
    var scope = "local scope";       // A local variable
    function f() { return scope; }   // Return the value in scope here
    return f();
}
checkscope()                         // => "local scope"

The checkscope() function
declares a local variable and then defines and invokes a function that
returns the value of that variable. It should be clear to you why the
call to checkscope() returns “local
scope”. Now let’s change the code just slightly. Can you tell what
this code will return?

var scope = "global scope";          // A global variable
function checkscope() {
    var scope = "local scope";       // A local variable
    function f() { return scope; }   // Return the value in scope here
    return f;
}
checkscope()()                       // What does this return?

In this code, a pair of parentheses has moved from inside
checkscope() to outside of it.
Instead of invoking the nested function and returning its result,
checkscope() now just returns the
nested function object itself. What happens when we invoke that nested
function (with the second pair of parentheses in the last line of
code) outside of the function in which it was defined?

Remember the fundamental rule of lexical scoping: JavaScript
functions are executed using the scope chain that was in effect when
they were defined. The nested function f() was defined under a scope chain in which
the variable scope was bound to the
value “local scope”. That binding is still in effect when f is executed, wherever it is executed from.
So the last line of code above returns “local scope”, not “global
scope”. This, in a nutshell, is the surprising and powerful nature of
closures: they capture the local variable (and parameter) bindings of
the outer function within which they are defined.

Implementing Closures

Closures are easy to understand if you simply accept the
lexical scoping rule: functions are executed using the scope chain
that was in effect when they were defined. Some programmers find
closures confusing, however, because they get caught up in
implementation details. Surely, they think, the local variables
defined in the outer function cease to exist when the outer function
returns, so how can the nested function execute using a scope chain
that does not exist anymore? If you’re wondering about this
yourself, then you have probably been exposed to low-level
programming languages like C and to stack-based CPU architectures:
if a function’s local variables are defined on a CPU stack, then
they would indeed cease to exist when the function returned.

But remember our definition of scope chain from The Scope Chain. We described it as a list of objects, not a
stack of bindings. Each time a JavaScript function is invoked, a new
object is created to hold the local variables for that invocation,
and that object is added to the scope chain. When the function
returns, that variable binding object is removed from the scope
chain. If there were no nested functions, there are no more
references to the binding object and it gets garbage collected. If
there were nested functions defined, then each of those functions
has a reference to the scope chain, and that scope chain refers to
the variable binding object. If those nested functions objects
remained within their outer function, however, then they themselves
will be garbage collected, along with the variable binding object
they referred to. But if the function defines a nested function and
returns it or stores it into a property somewhere, then there will
be an external reference to the nested function. It won’t be garbage
collected, and the variable binding object it refers to won’t be
garbage collected either.

In Defining Your Own Function Properties we defined a uniqueInteger() function that used a
property of the function itself to keep track of the next value to be
returned. A shortcoming of that approach is that buggy or malicious
code could reset the counter or set it to a noninteger, causing the
uniqueInteger() function to violate
the “unique” or the “integer” part of its contract. Closures capture
the local variables of a single function invocation and can use those
variables as private state. Here is how we could rewrite the uniqueInteger() function using
closures:

var uniqueInteger = (function() {          // Define and invoke
                         var counter = 0;  // Private state of function below
                         return function() { return counter++; };
                     }());

In order to understand this code, you have to read it carefully.
At first glance, the first line of code looks like it is assigning a
function to the variable uniqueInteger. In fact, the code is defining
and invoking (as hinted by the open parenthesis on the first line) a
function, so it is the return value of the function that is being
assigned to unique Integer. Now, if we study the body of
the function, we see that its return value is another function. It is
this nested function object that gets assigned to unique Integer. The nested
function has access to the variables in scope, and can use the
counter
variable defined in the outer function. Once that outer function
returns, no other code can see the counter variable: the inner function has
exclusive access to it.

Private variables like counter need not be exclusive to a single
closure: it is perfectly possible for two or more nested functions to
be defined within the same outer function and share the same scope
chain. Consider the following code:

function counter() {
    var n = 0;
    return {
        count: function() { return n++; },
        reset: function() { n = 0; }
    };
}

var c = counter(), d = counter();   // Create two counters
c.count()                           // => 0
d.count()                           // => 0: they count independently
c.reset()                           // reset() and count() methods share state
c.count()                           // => 0: because we reset c
d.count()                           // => 1: d was not reset

The counter() function
returns a “counter” object. This object has two methods: count() returns the next integer, and
reset() resets the internal state.
The first thing to understand is that the two methods share access to
the private variable n. The second
thing to understand is that each invocation of counter() creates a new scope chain and a
new private variable. So if you call counter() twice, you get two counter objects
with different private variables. Calling count() or reset() on one counter object has no effect
on the other.

It is worth noting here that you can combine this closure
technique with property getters and setters. The following version of
the counter() function is a
variation on code that appeared in Property Getters and Setters, but it uses closures for private state
rather than relying on a regular object property:

function counter(n) {  // Function argument n is the private variable
    return {
        // Property getter method returns and increments private counter var.
        get count() { return n++; },
        // Property setter doesn't allow the value of n to decrease
        set count(m) {
	    if (m >= n) n = m;
	    else throw Error("count can only be set to a larger value");
        }
    };
}

var c = counter(1000);
c.count            // => 1000
c.count            // => 1001
c.count = 2000
c.count            // => 2000
c.count = 2000     // => Error!

Note that this version of the counter() function does not declare a local
variable, but just uses its parameter n to hold the private state shared by the
property accessor methods. This allows the caller of counter() to specify the initial value of
the private variable.

Example 8-4 is a generalization of
the shared private state through closures technique we’ve been
demonstrating here. This example defines an addPrivateProperty() function that defines a
private variable and two nested functions to get and set the value of
that variable. It adds these nested functions as methods of the object
you specify:

Example 8-4. Private property accessor methods using closures

// This function adds property accessor methods for a property with
// the specified name to the object o.  The methods are named get<name>
// and set<name>.  If a predicate function is supplied, the setter
// method uses it to test its argument for validity before storing it.
// If the predicate returns false, the setter method throws an exception.
//
// The unusual thing about this function is that the property value
// that is manipulated by the getter and setter methods is not stored in
// the object o.  Instead, the value is stored only in a local variable
// in this function.  The getter and setter methods are also defined
// locally to this function and therefore have access to this local variable.
// This means that the value is private to the two accessor methods, and it 
// cannot be set or modified except through the setter method.
function addPrivateProperty(o, name, predicate) {
    var value;  // This is the property value

    // The getter method simply returns the value.
    o["get" + name] = function() { return value; };

    // The setter method stores the value or throws an exception if
    // the predicate rejects the value.
    o["set" + name] = function(v) {
        if (predicate && !predicate(v))
            throw Error("set" + name + ": invalid value " + v);
        else
            value = v;
    };
}

// The following code demonstrates the addPrivateProperty() method.
var o = {};  // Here is an empty object

// Add property accessor methods getName and setName()
// Ensure that only string values are allowed
addPrivateProperty(o, "Name", function(x) { return typeof x == "string"; });

o.setName("Frank");       // Set the property value
console.log(o.getName()); // Get the property value
o.setName(0);             // Try to set a value of the wrong type

We’ve now seen a number of examples in which two closures are
defined in the same scope chain and share access to the same private
variable or variables. This is an important technique, but it is just as
important to recognize when closures inadvertently share access to a
variable that they should not share. Consider the following
code:

// This function returns a function that always returns v
function constfunc(v) { return function() { return v; }; }

// Create an array of constant functions:
var funcs = [];
for(var i = 0; i < 10; i++) funcs[i] = constfunc(i);

// The function at array element 5 returns the value 5.
funcs[5]()    // => 5

When working with code like this that creates multiple closures
using a loop, it is a common error to try to move the loop within the
function that defines the closures. Think about the following code,
for example:

// Return an array of functions that return the values 0-9
function constfuncs() {
    var funcs = [];
    for(var i = 0; i < 10; i++) 
       funcs[i] = function() { return i; };
    return funcs;
}

var funcs = constfuncs();
funcs[5]()    // What does this return?

The code above creates 10 closures, and stores them in an array.
The closures are all defined within the same invocation of the
function, so they share access to the variable i. When constfuncs() returns, the value of the
variable i is 10, and all 10
closures share this value. Therefore, all the functions in the
returned array of functions return the same value, which is not what
we wanted at all. It is important to remember that the scope chain
associated with a closure is “live.” Nested functions do not make
private copies of the scope or make static snapshots of the variable
bindings.

Another thing to remember when writing closures is that this is a JavaScript keyword, not a
variable. As discussed earlier, every function invocation has a
this value, and a closure cannot
access the this value of its outer
function unless the outer function has saved that value into a
variable:

var self = this;  // Save this value in a variable for use by nested funcs.

The arguments binding is
similar. This is not a language keyword, but it is automatically
declared for every function invocation. Since a closure has its own
binding for arguments, it cannot access the outer
function’s arguments array unless the outer function has saved that
array into a variable by a different name:

var outerArguments = arguments;  // Save for use by nested functions

Example 8-5, later in this chapter, defines a
closure that uses these techniques to refer to both the this and arguments values of the outer
function.


[13] This is an old term that refers to the fact that the
function’s variables have bindings in the scope chain and that
therefore the function is “closed over” its variables.

Comments are closed.

loading...