Ian
Ian

Reputation: 4457

Lexical Scope/Closures and Global Function Recursion

This is both an example of lexical scoping and a question to confirm my own understanding. First, consider the following example:

Html:

<div id="testtxt"></div>

JS:

function fnTest(currentIdx, endIdx) {
    $('#testtxt').html($('#testtxt').html() + 'Function Called ' + currentIdx + '<br />');
    if (currentIdx < endIdx) {
        setTimeout(function(){ 
            fnTest(currentIdx + 1, endIdx); 
        }, 100);
    }
}

fnTest(1, 10);
fnTest(11, 20);

Output:

Function Called 1
Function Called 11
Function Called 2
Function Called 12
Function Called 3
Function Called 13
Function Called 4
Function Called 14
Function Called 5
Function Called 15
Function Called 6
Function Called 16
Function Called 7
Function Called 17
Function Called 8
Function Called 18
Function Called 9
Function Called 19
Function Called 10
Function Called 20

When I first ran this example I was worried that there would be a single global closure for fnTest and therefore currentIdx and endIdx would be getting set and accessed by both calls of fnTest. However this is not the case.

Please let me know if the following is a good way to explain it:

Each call to fnTest creates a unique object in which the variables currentIdx and endIdx are stored for the lifetime of that call and all sub-routines within that call (this is called a closure). The setTimeout call creates a new object from the anonymous function, which has access to the fnTest closure and can therefore reference currentIdx and endIdx, this object/function will execute after a 100ms delay. Upon execution the anonymous function will itself create a new fnTest closure by calling fnTest. At this point the original fnTest closure referenced by the anonymous function may be disposed of.

Please correct my technical terminology where necessary.

Upvotes: 0

Views: 161

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1075009

Basically right, a few points:

Each call to fnTest creates a unique object in which the variables currentIdx and endIdx are stored for the lifetime of that call...

For the lifetime of that object, which as Felix said is called an environment. That lifetime is like the lifetime of all other objects: As long as something still has a reference to it. In particular, it continues (in this case) after fnTest has returned.

The only things that can have references to these environment objects are the functions created within them, which are called closures (they "close over" the environment).

...and all sub-routines within that call (this is called a closure)

The functions are called closures, not the environment.

The setTimeout call creates a new object from the anonymous function

No, your code is creating an anonymous function and passing a reference to that function into setTimeout.

...which has access to the fnTest closure and can therefore reference currentIdx and endIdx

it has access to the environment where it was created

this object/function will execute after a 100ms delay. Upon execution the anonymous function will itself create a new fnTest closure by calling fnTest.

It creates a new environment by calling fnTest, yes.

At this point the original fnTest closure referenced by the anonymous function may be disposed of.

Since the timer mechanism has released its reference to the anonymous function, nothing references the anonymous function anymore, and it can be garbage-collected. Since it's the only thing referencing the environment from the initial call to fnTest, that environment can also be garbage-collected.

We're doing a little bit of hand-waving around details in the above, but the important concepts are present and correct.

Upvotes: 2

Related Questions