Handling interactions between closures
When more than one inner function exists, closures can have effects that are not as easy to anticipate. Suppose we pair our incrementing function with another function, this time incrementing by two:
function outerFn() { var outerVar = 0; function innerFn1() { outerVar++; console.log('(1) outerVar = ' + outerVar); } function innerFn2() { outerVar += 2; console.log('(2) outerVar = ' + outerVar); } return {'fn1': innerFn1, 'fn2': innerFn2}; } var fnRef = outerFn(); fnRef.fn1(); fnRef.fn2(); fnRef.fn1(); var fnRef2 = outerFn(); fnRef2.fn1(); fnRef2.fn2(); fnRef2.fn1();
Listing A.9
We return references to both functions, using an object to do so (this illustrates another way in which a reference to an inner function can escape its parent). Both functions are called through the references:
(1) outerVar = 1 (2) outerVar = 3 (1) outerVar = 4 (1) outerVar = 1 (2) outerVar = 3 (1) outerVar = 4
The two inner functions refer to the same local variable, so they share the same closing environment. When innerFn1()
increments outerVar
by 1
, this sets the new starting value of outerVar
when innerFn2()
is called and vice versa. Once again, we see that any subsequent call to outerFn()
creates new instances of these closures with a new closing environment to match. Those familiar with object-oriented programming will note that we have in essence created a new object, with the free variables acting as instance variables and the closures acting as instance methods. The variables are also private, as they cannot be directly referenced outside of their enclosing scope, enabling true object-oriented data privacy.