Just JavaScript - Functions Scope, Lifetime and Closure
Written by Ian Elliot   
Monday, 30 April 2018
Article Index
Just JavaScript - Functions Scope, Lifetime and Closure
Closure

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. 

For example: 

var myFunction= function(a,b){
 var ans=a+b;
 this.myInnerF=function(){
   alert(ans);
  };
 return ans;
};

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:

myFunction(1,2);

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. 

For example:

this.myInnerF();

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.

Closures

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:

var myFunction= function(){
  var message="Hello Closure";
   this.myInnerF=function(){
                   alert(message);
                   message="Goodbye Closure";
                 };
};

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;

myFunction();

This evaluates the body of the function and hence results in the creation of the Function object with the function body:

 this.myInnerF=function(){
                 alert(message); 
                 message="Goodbye Closure";
               };

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:

this.myInnerF();

you will see:

"Hello Closure"

and the second time you evaluate it: 

"Goodbye Closure" 

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:

var myFunction= function(){
   this.myInnerF=function(){
                   alert(message);
                   message="Goodbye Closure";
                 };
   var message="Hello Closure";
};

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:

var myFunction= function(){
  var message;
  this.myInnerF=function(){ 
                  alert(message); 
                  message="Goodbye Closure";
                };
  message="Hello Closure"; 
};

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

An Example

final version in print book

Block Scope

final version in print book

Summary

  • A Function object can be created by the evaluation of a function body. This is often described as nesting one function inside another, but it is important to realize that this nesting is really only about the way the contained function body can access variables in the containing function body.

  • Like all JavaScript objects, a Function object exists until there are no references to it when it is automatically garbage collected. 

  • Variables declared within a function are local to that function and are created when the function is executed and destroyed when it ends. However, closures modify this behavior.

  • All of the variables declared by any enclosing functions are in scope and can be accessed by an inner function body. This is called nested scope.

  • All of the variables that are in scope when the Function object is created remain accessible whenever the function body is evaluated and this includes times when the relevant variables would have been destroyed by the normal rules. This is a closure.

  • The idea of a closure is a natural consequence of using Function objects with differing lifetimes and nested scope.

  • Each time a function is executed, an execution context is created and this is shared by all of the functions created by that function.

  • The variables in the execution context all have their final value when accessed as part of a closure, but they can be changed. Hence they are privates shared variables.

  • ES2015 introduces block scoped variables using let and const. These can be used to force a new execution context to be created.

 

This is an extract from the book Just JavaScript by Ian Elliot.

Buy Now: from your local Amazon

Just JavaScript 
An Idiomatic Approach

JustJavaScriptSmall

A Radical Look At JavaScript

 

Most books on JavaScript either compare it to the better known class based languages such as Java or C++ and even go on to show you how to make it look like the one of these.

Just JavaScript is an experiment in telling JavaScript's story "just as it is" without trying to apologise for its lack of class or some other feature. The broad features of the story are very clear but some of the small details may need working out along the way - hence the use of the term "experiment". Read on, but don't assume that you are just reading an account of Java, C++ or C# translated to JavaScript - you need to think about things in a new way. 

Just JavaScript is a radical look at the language without apologies.

Contents

  1. JavaScript – Essentially Different
  2. In The Beginning Was The Object
  3. Real World Objects 
  4. The Function Object
          Extract - The Function Object
          Extract - Function Object Self Reference
  5. The Object Expression
  6. Function Scope, Lifetime & Closure
    Extract Scope, Lifetime & Closure
    Extract Execution Context ***NEW!
  7. Parameters, Returns and Destructuring
         Extract - Parameters, and Destructuring
  8. How Functions Become Methods
  9. Object Construction
         Extract: - Object Factories
  10. The Prototype
         Extract - ES2015 Class and Extends
  11. Inheritance and Type
  12. The Search For Type
  13. Property Checking

Buy Now: from your local Amazon

Also by Ian Elliot 
JavaScript Async: Events, Callbacks, Promises and Async Await
Just jQuery: The Core UI 
Just jQuery: Events, Async & AJAX  

<ASIN:1871962579>

<ASIN:1871962560>

<ASIN:1871962501>

<ASIN:1871962528>

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

espbook

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Monday, 21 May 2018 )