|Monday, 30 April 2018|
Page 2 of 2
Now consider what happens if the inner function has a variable that references it that isn't local to the outer function.
In this case the inner function can actually live longer than the outer function because it still has a variable that references it and hence it won’t be garbage collected.
That is, not only can the inner function object exist after the outer function has completed, it can exist when the outer function object has been destroyed. Their lifetimes are quite independent of one another.
This is yet another strange idea but perfectly natural if you think in terms of Function objects.
Function objects stay alive and potentially active as long as there is a variable that references them.
Sounds complicated – but you get used to it.
Now the inner function is referenced by a Global variable – recall that this references the Global object – to be more precise it references the execution context but more of this later.
Note: if you want to use this in strict mode make sure you declare myInnerF as a global variable outside of the function.
Now we can call the outer function:
and when the function returns the global variable, myInner references the Function object created within the outer function.
As there is still a reference to the inner Function object it isn't garbage collected and so still exists after the outer function has finished evaluating.
What this means is that you can still evaluate the inner function long after the function body that created it has finished executing and perhaps even long after the outer Function object that declares its function body has been destroyed.
will display the Alert box and you will see the value in ans at the time the inner function was created, even though the outer function has finished running some time ago and all its local variables no longer exist – recall local variables only exist while the function is being evaluated.
You can see that by allowing functions to have nested scope we have to allow the inner function to use local variables belonging to the outer function even after those local variables have been destroyed because the outer function has completed.
OK this is all fine, but how can the inner function have access to a variable – ans – that doesn't exist any more?
This is what closures are all about.
As the lifetime of a Function object only depends on what variables reference it, just like any other object, it can exist well after any outer function that created it.
By the rules of nested scope the inner function has access to the variables in the outer function when it is executed.
The only problem with this is that the local variables of the outer function only exist while it is executing and this means that in principle the inner function can only access them if it is executed as part of the outer function’s code i.e. only during the time that the outer function is executing.
The idea of a closure simply extends this access to all times.
In other words nested scope persists beyond the evaluation of the outer function and even beyond the lifetime of the outer function and all its local variables.
If this wasn't the case then having inner Functions with their own lifetimes and nested scope would be very complicated.
The rule would have to be something like - an inner function has access to the local variables of an outer function but only while the function is being evaluated. If you evaluated the inner function at some later time then you would simply get an error message because variables that you could access at one time are no longer accessible.
This would mean that you would get a different result according to when and where you evaluated the inner function.
Using a closure means that the inner function always has access to the same variables no matter when or where it is evaluated.
To be more precise, when a Function object is created all of the variables which are in scope, including those of any outer functions, are stored in an execution context – this what was explained right at the start.
What is new is that the execution context stays with the Function object even when the variables no longer exist because the function body which created them has finished running or the Function object that it belongs to has been garbage collected.
Put another way- a Function’s execution context remains its execution context as long as it exists.
There is an important exception to this rule.
Functions created using the Function constructor are always created in the global scope and they do not create closures using the current execution context.
As another example consider:
The first line of myFunction creates a local variable message which is added to the execution context and set to "Hello Closure". Next an inner function is defined and a reference to it is stored in the Global variable myInnerF. The function body shows an alert box with what is stored in message and then it changes the message. This demonstrates that you can access and change what is stored in a variable that is provided courtesy of a closure.
With this definition you can now do the following. First you can execute myFunction;
This evaluates the body of the function and hence results in the creation of the Function object with the function body:
Notice once again that the inner function is not evaluated, this just sets up the Function object and its function body.
Now you can set the variable myFunction to null which results in there being no more references to the outer Function object and so it is garbage collected – that is its Function object no longer exists and the message variable should no longer exist.
Even so you can still evaluate the inner Function object. The first time:
you will see:
and the second time you evaluate it:
It seems the message variable is alive and well, even if the Function object and its function body that is it local to are most certainly not.
When you first encounter the idea of a closure it seems complicated and arbitrary – why do thing in this way?
Why invent the idea of a closure?
Once you follow the fact that functions are just objects, and a Function object can live longer than the Function object that created it, and there is nested scope you begin to see why closures are natural.
They are a natural consequence of letting an inner function access the variables of all of the outer functions that contain it.
To make this work a Function object has to capture all of the variables that are in scope when it is created and their values when the outer functions have completed.
This last point is subtle.
See if you can work out what happens if we make a small change to the last example and if you can also work out why it happens:
The only change is that now message is defined after the inner function. You might reason that message isn't in scope but that would be to forget hoisting.
If you recall, all variable declarations are moved by the compiler to the top of the function they occur in.
So the code is equivalent to:
This means that the variable is in scope, and even though it isn't assigned until the end of the outer function, i.e. after the definition of the inner function, its final value is part of the closure. The reason is that the execution context stores the variables and their changing values until the outer function stops.
That is, the execution context captures the final value of the outer functions local variables.
So the new version still displays the two messages as before.
Execution Contexts Are Shared
final version in print book
final version in print book
final version in print book
Buy Now: from your local Amazon
|Last Updated ( Monday, 21 May 2018 )|