Just JavaScript - Execution Context
Written by Ian Elliot   
Monday, 29 July 2019
Article Index
Just JavaScript - Execution Context
How It Goes Wrong

An Example

Understanding the idea of a shared or non-shared execution context can help avoid errors such as:

var myFunction = function () {
                   this.f = new Array(5);
                   for (var i = 0; i < 5; i++) {
                     this.f[i] = function () {
                                   alert(i);
                                 };
                   }
                 };

The intention here is to create an array of five functions which when called displays the current value of i when the function was created.

Can you see what is wrong?

The execution context has the variable i in it, but it is shared between the five functions. When any of the functions are called it has the value 5 which is the last value it was assigned in myFunction. As a result if you try:

        myFunction();
        for (i = 0; i < 5; i++) {
            f[i]();
        }

You will see 5 displayed five times which is not what is required or perhaps expected.

There are various ways of making this work as required but in ES5 it is tricky. If you want to let each inner function capture the current value of i when the function is defined then you have little choice but to create a separate execution context for each of the inner functions.

The simplest way to do this is to introduce another function which creates the original inner functions:

var myFunction = function () {
                   this.f = new Array(5);
                   for (var i = 0; i < 5; i++) {
                     (function context() {
                         var j=i;
                         this.f[i] = function () {
                                       alert(j);
                                     };
                       })();
                   }
                 };

The overall plan of the code is much the same as before but now the function context constructs the inner functions. It declares a new variable j which is included in the execution context of the inner functions. Notice that the context function is executed immediately because of the final parentheses.

This means that the Function object corresponding to context is created and executed each time through the for loop. This creates a separate execution context for each of the inner functions each with their own version of variable j.

If this seems complicated – it is, and it is inefficient in that two Function objects are created each time through the loop.

ES2015 has a much easier solution – block scope.

Block Scope

JavaScript before ES2015 only supported function scope variables. That is, no matter where you declared a variable its scope was the entire Function it was declared in. Not only this but all variable declarations are hoisted to the top of the Function.

ES2015 introduces block scoped variables with the keywords let and const. The only difference between let and const is that, as its name suggests, a variable created using const cannot be reassigned.

A block is a compound statement enclosed by curly brackets and you can use let or const to declare variables that are in scope only within the block.

For example:

var myVar = 1;
alert(myVar);
{
   let myVar = 2;
   alert(myVar);
}            
alert(myVar);

This displays 1 then 2 and then 1 again. Within the block myVar in the outer scope is no longer accessible as the block scope version of myVar is.

The same thing works with const but in this case you wouldn’t be able to change the value of myVar once set.

Now we come to the question of hoisting.

Block scope variables are hoisted, but only to the start of their scope i.e. the block that contains them. They are also not initialized to undefined but left uninitialized. This might seem like a strange thing to do, but it is designed to block variables in the outer scope being accessible within the block before the block variable is declared.

For example:

var myVar = 1;
{
  alert(myVar);
  let myVar = 2;
  alert(myVar);
}  

If it wasn’t for the declaration of myVar within the block, the outer scoped myVar would be accessible within the block. You might think that the first alert would display the current value of the outer myVar but instead it generates a runtime error that tells you myVar isn’t defined. This is because the declaration of let myVar is hoisted to the start of the block but the variable is left undefined until the code reaches the assignment.

Because of hoisting, block scope variables override outer scope variables of the same name throughout the block irrespective of where they are declared.

There is another very important difference between function and block scoped variables. You can declare a function scoped variable as often as you like and it has no effect:

var myVar=1;
var myVar=2;

is fine and equivalent to:

var myVar=1;
myVar=2;

However, you can only declare a block scope variable once:

{
 let myVar=1;
 let myVar=2;
}

generates a runtime error:

Identifier 'myVar' has already been declared

The same is true for const.

You might not be able to declare a block scoped variable twice, but you can declare one within a loop or in the head of a loop. In this case the declaration causes a new instance of the variable to be created. This means you can use a block scoped variable within a loop and use it to create a unique execution context each time.

For example we can write the example in the previous section much more simply using let:

var myFunction = function () {
                   this.f = new Array(5);
                   for (var i = 0; i < 5; i++) {
                     let j = i;
                     this.f[i] = function () {
                                   alert(j);
                                 };
                   }

};

This works and each function now gets its own instance of the variable j. You can also write the function as:

var myFunction = function () {
                   this.f = new Array(5);
                   for (let i = 0; i < 5; i++) {
                     this.f[i] = function () {
                                   alert(i);
                                 };
                   }
                 }

Again the execution context has a new instance of i each time through the loop. This also works with const. Notice that neither example works if you change let to var.

There are some other subtle behaviors caused by block scoping – for example block scoped top level variables are not global properties. It is also worth mentioning that function statements are also block scoped in ES2015 but hoisted in the usual way to the start of their scope.

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, 29 July 2019 )