Javascript Tutorial
Javascript Tutorial Introduction to Javascript JavaScript Code Editors & IDE JavaScript Hello World Example Javascript Syntax and Rules syntax_rules javascript_identifiers JavaScript Keywords & Reserved Words javascript_variables JavaScript Const JavaScript let vs var vs const Data Types in JavaScript JavaScript String Template Literals & String interpolation in JavaScript Tagged Templates in JavaScript String to Number in JavaScript Number Data Type in JavaScript NaN in JavaScript JavaScript Number Min & Max & Safe Values JavaScript EPSILON & Floating point precision Infinity in JavaScript JavaScript Bigint BigInt Vs Number in JavaScript Boolean Data Type in JavaScript Undefined in JavaScript Null in JavaScript Null vs Undefined in JavaScript JavaScript Operators Arithmetic Operators in JavaScript Unary plus & minus operators in JavaScript Increment & Decrement Operators in JavaScript Comparison or Relational operators in JavaScript Strict Equality (==) Loose Equality (===) in JavaScript Ternary Conditional Operator in JavaScript Logical Operators in JavaScript Bitwise Operators in JavaScript Assignment Operators in JavaScript Nullish Coalescing Operator in JavaScript Comma Operator in JavaScript Typeof JavaScript Operator Precedence in JavaScript JavaScript if, else & nested if statement Switch Statement in JavaScript While & Do While Loops in JavaScript For Loop in JavaScript Break statement in JavaScript Continue Statement in JavaScript Arrays in JavaScript Array Constructor in Javascript Sparse Array Vs Dense Array in JavaScript How to merge Arrays in JavaScript Array Methods in JavaScript Functions in JavaScript Function Parameters & Arguments in JavaScript JavaScript Default Parameters Pass by Value and Pass by Reference in Javascript Function Expression in Javascript Nested Functions in JavaScript Immediately-invoked Function Expressions (IIFE) JavaScript Callback Functions Arrow Functions in JavaScript Arguments Object In JavaScript Rest Parameters in JavaScript Objects in Javascript Create Objects in JavaScript JavaScript Object Properties Computed Property Names in JavaScript Object Literal in JavaScript Constructor Function & New Operator in JavaScript Delete Operator in JavaScript hasOwnProperty in JavaScript Using Getters and Setters in Javascript DefineProperty in JavaScript JavaScript Property Descriptors Enumerable, Writable & Configurable Object Destructuring in JavaScript Variable Scope in JavaScript Hoisting in JavaScript Lexical Scope & Closures in JavaScript This in JavaScript Global Object, Window & Globalthis in JavaScript Call function in Javascript Prototype In Javascript Prototype Inheritance in JavaScript Instanceof Operator in JavaScript Spread Operator in JavaScript

Lexical Scope & Closuresin JavaScript

JavaScript uses the Lexical Scope to resolve the variable names when we create functions inside another function. The lexical means that JavaScript determines the parent scope of a function by looking at where we created the function and not where we invoked it. But JavaScript allows passing a function around, which means we may invoke them in a different scope than the original scope. Hence losing the connection to its parent scope. JavaScript solves this problem by creating Closures. A Closure contains function along with reference to its parent scope allowing the function to stay connected with its parent scope.

Scopes

We can access the JavaScript variables only within the scope in which we declare them. JavaScript creates Global, Module, Function, or Block scope depending on where we declare our variable. We learned all these in our tutorial Scopes in JavaScript.

When we execute a script, the JavaScript creates a Global scope. Global scope is the root scope of all other scopes.

Whenever we invoke a function (or code block ({ }) since ES6), JavaScript creates a new local scope for them. We call it local scope because variables declared inside them cannot be used outside. The scope act as boundaries for the variables

JavaScript creates these scope as child scope of the global scope.

In the code below, the variable greetText in the greeting is only available inside that function. Trying to access it outside the function results in Uncaught ReferenceError: greetText is not defined error.

            
function greeting() {
  let greetText="Hello"
  console.log(greetText)
}
 
greeting(); 
 
 
console.log(greetText)  //Uncaught ReferenceError: greetText is not defined error
                
        
    

The code above creates two scopes. global Scope when the script starts to execute and function scope when we invoke the greeting function.

Similarly, the code blocks also create a new scope. We cannot access the greetText declared inside the code block outside the code block.

            
{
  let greetText="Hello"
  console.log(greetText)
}
 
 
console.log(greetText)  //Uncaught ReferenceError: greetText is not defined error

                
        
    

Note that variables declared with var keyword ignores the code block. Hence the following code does not throw any errors.

            
{
  var greetText="Hello"     //var ignores the code block
  console.log(greetText)
}
 
 
console.log(greetText)  //Hello

                
        
    

Any variable declared outside the code block or function belongs to the global scope. You can access it anywhere in the application.

Also, remember JavaScript creates scopes when we invoke them not when we declare them. In the code below, we invoke greeting twice. JavaScript creates new scope on each call to greeting. Both these scopes are independent of each other.

            
function greeting() {
  let greetText="Hello"
  console.log(greetText)
}
 
greeting();   //a new function scope created here
greeting();   //another function scope created here.

                
        
    

Garbage collection

JavaScript automatically allocates memory when it creates variables and frees it when it no longer needs it. This freeing up of the memory happens behind the scene and we call that garbage collection.

In the code below, when we invoke the greeting function JavaScript creates a new scope for it. The variable greetText is created inside that scope. Once the function returns, we no longer need the scope & the variable. Hence JavaScript frees up the memory by removing them via garbage collection.

            
function greeting() {
  let greetText="Hello"
  console.log(greetText)
}
 
greeting(); //a new function scope created here

                
        
    

Variable Resolution

When we refer to a variable, JavaScript always starts to look for it in the current scope i.e. scope of the currently executing function or block.

What if it does not find there?. It will look for it in its parent Scope.

If it finds the variable in Parent Scope, then it will return it. It will not search any further.

But if it fails then it will continue to search in its parent scope. It will continue until it reaches the global scope. if it does not find it in the global scope, then it will throw the Uncaught ReferenceError.

But how does JavaScript determines which is the parent scope of the currently executing function?.

There are two ways in which modern programming languages can determine the parent scope of a function. They are

  1. Lexical Scope (static Scope)
  2. Dynamic Scope

The JavaScript uses the lexical scope to determine the parent scope.

Lexical Scope

In Lexical Scope, the child scope accesses the variable defined in the parent scope lexically. The lexically means that JavaScript determines the parent scope by looking at where we created the function and not where we invoked it.

Let us find out what is lexical scope using few code examples.

Lexical Scope Example

JavaScript allows us to create functions inside functions ( neste functions ), block statements inside another block statement, block statements inside afunction or functions inside a block statement, etc.

JavaScript creates new scope for every function and code block that it executes. When we create a nested function (or inner function) inside another function (outer function), the scope of the outer function becomes the parent scope of the inner function.

In the following example, when we invoke the greeting function it creates a new scope. Inside the greeting functions scope, we have created the sayHi function.

        

let greetText="Hello from global"
 
 function greeting() {
  
   let greetText="Hello"
  
   //Creation of sayHi function inside the greeting Scope
   //Hence the greeting scope is the parent scope of sayHi
   sayHi= function() {
     console.log(greetText);
   }
  
   //Invoking sayHi
   sayHi();   //Hello
 }
  
 //Invoking the greeting function. 
 //A new scope is created for greeting
 greeting(); 
  
    
        
    

The rules of lexical scope say that the parent scope of a function is the scope where we create that function. Since we have created the sayHi function inside the greeting function, the current scope of greeting function becomes the parent scope of the sayHi function.

When we invoke the sayHi function it creates a new scope. Inside sayHi function, we try to access the greetText variable. The variable resolution starts from the sayHi scope. Since JavaScript does not find it there, it will move up to its parent scope ( i.e greeting scope). The search ends there as it will find the variable there.

The following example has three functions. funcGrandChild is inside the funcChild, which in turn inside the funcParent.

        
 
//Global Variable. Accessible everywhere
let x = 10
 
function funcParent() {
 
  let a = 1;
 
  funcChild = function () {
 
    let b = 2
  
    funcGrandChild = function () {
      let c = 3
      console.log("funcGrandChild",x,a,b,c)
    }
 
    funcGrandChild()
 
    console.log("funcChild",x,a,b)
 
  }
 
  funcChild()
  console.log("funcParent",x,a)
 
}
 
funcParent();
 
 
***** Result *****
funcGrandChild 10 1 2 3
code1.js:19 funcChild 10 1 2
code1.js:24 funcParent 10 1
    
        
    

There are four scopes in the above example code. function scope for each of the three functions plus one global scope. The funcChild can access the variable a defined in its parent function i.e. funcParent. The funcGrandChild can access the variable b defined in its parent function i.e. funcChild. It can also access the variable a defined in its grandparent function i.e. funcParent.

But there is a catch

The above are simple examples. We create a function inside another function and then we immediately invoked it.

In JavaScript, we can assign a function to a variable and pass it around. This enables us to invoke the function in a different scope than the original scope in which we created it.

Take a look at the following code

In the code below we have declared the sayHi function inside the greeting1. But we have invoked it inside the greeting2 function. Both greeting1 & greeting2 contain the variable greetText.

        

let greetText="Hello from global"
 
function greeting1() {
 
  let greetText="Hello"
 
  // creating the sayHi function. We will not invoke it
  sayHi= function() {
    console.log(greetText);
  }
 
  return sayHi;
}
 
 
sayHi=greeting1();   //new scope is created here 
 
function greeting2() {
 
  let greetText="Hi"
 
  sayHi();    //invoking it inside the scope of greeting2
 
}
 
 
greeting2(); // new scope is created here
    
        
    

JavaScript creates a global scope when we execute the above code. The codelet greetText="Hello from global" creates the global variable greetText because we are inside the global scope.

The greeting1 scope is created when we invoke the greeting1 function sayHi=greeting1();

The greeting1 function creates the variable greetText and sayHi function. Hence both will belong to the greeting1‘s scope

When we invoke greeting2(), we create a new scope for greeting2.

The greeting2 function also creates greetText variable let greetText="Hi". This greetText will not overwrite the greetText in the greeting1 as they are part of different scopes.

Now, we invoke the sayHi from the scope of greeting2. This will create a new scope for sayHi function.

The search for the variable greetText inside the sayHi function starts from the scope of the sayHi function. Since we have not declared it there, It looks for it in its parent scope. The parent scope of sayHi is the greeting1 scope, so the search continues there and it finds the variable and prints “Hello” in the console.

Alternatively, if when we determine the scope based on where we have invoked the function, we call that dynamic scope. In a dynamically scoped language, the value of the greetText in the sayHi function will come from the greeting2 function Because that is where we invoked it. Dynamically scoped language determines the scope dynamically at run time and scope may also change from one invocation to another.

Closures

The closure in JavaScript is a combination of the function object and the reference to the parent scope it belongs to. JavaScript creates a closure every time we create afunction, at the time of the creation of the function. The closure is what gives the function access to its parent scope.

To understand the closure let us take a look at the sayHi example from the previous section

We created the sayHi function in the scope of greeting1 function. The greeting1 function does not invoke the sayHi but returns it to the caller and then it finishes its execution. When the function returns, we expect JavaScript to run a garbage collection and clean up the memory.

But when we invoke the sayHi in the scope of greeting2 function, It prints the value of greetText from the greeting1 function (function in which we created it) and not from the greeting2 function (function in which we invoked it).

Hence it is clear that although the greeting1 has finished executing, its variable greetText is still available to sayHi function. This means JavaScript has not removed the scope of greeting1 from the memory.

This is because JavaScript creates a closure when we create the function.

        
let greetText="Hello from global"
 
 function greeting1() {
  
   let greetText="Hello"
  
   //function created here. Hence it value of greetText from this scope
   sayHi= function() {
     console.log(greetText);
   }
  
   return sayHi   //returning sayHi and not invoking it
   
 }
  
 funcSayHi = greeting1(); 
  
 function greeting2() {
  
   let greetText="Hi"    
  
   //Function Invoked in scope of greeting2. 
   //But it is still connected to the scope of greeting1 via Closures 
   funcSayHi()   
 }
  
 greeting2()   //Hello
    
        
    

How Closures work

JavaScript creates a closure every time we create a function, at the time of the creation of the function.

The functions are objects in JavaScript. Just like any other object, you can store them in a variable. So when we create a function, it creates a variable sayHi and stores the code of the function there. The function also has a hidden property named [[Environment]]. We cannot access it from the code. JavaScript uses it to store the reference to the current executing parent scope of the function.

For Example, in the code above invoking the sayHi = greeting1(); creates a new scope. The code inside the greeting1 runs in that new scope. So when we create the sayHi function, JavaScript saves the reference to the greeting1 along with the function in the sayHi variable

Hence a function always carries a reference to its parent scope. and stay connected to their parent scope always. Since the function carries a reference to its parent scope, JavaScript will not be able to clean up the parent scope. Parent scope stays in memory as long there is a function that has a reference to it.

When we invoke a function, the Javascript first searches for the variables in the scope of the function itself. If it does not find it then it will look for it in the parent scope. To determine the parent scope it will always use the closure and not the current executing scope.

Closure Examples

The Closures do not copy the parent scope, but stores the reference to it. Hence if anyone modifies a variable in the parent scope, the inner function will see it.

In the following example, we create the startCount inside the scope of the counter function. At the time of the creation of the startCount, the value of the count variable is 10. We increase the count and return the startCount.

When we invoke the startCount later, it will print the latest value of the count (i.e. 11).

        
function counter() {
 
  let count = 10;
  startCount= function () {
    console.log(count);
  };
  count++;
  return startCount;
}
 
showCount= counter()
 
showCount() //11    
    
        
    

Creating Private Variables

The closure is useful in creating Private variables or hiding implementation detail from the user, which is not possible only using the objects.

In the following example, makeCounter function creates a new instance of the counterObj and returns it. The object has two functions increment & decrement. Since they are created inside the makeCounter function, they share the same parent scope.

        
function makeCounter(counterName) {
  let count = 0;
  let name= counterName
 
  let counterObj = {
      
      increment: function () {
          ++count;
          console.log("%s :  %d",name,count)
          return count
      },
      decrement: function () {
        --count;
        console.log("%s :  %d",name,count)
        return count
    },
  }
 
  return counterObj 
}
 
 
counter1=makeCounter("Counter1")
 
counter1.increment()   //Counter1 : 1
counter1.increment()   //Counter1 : 2
counter1.increment()   //Counter1 : 3
counter1.decrement()   //Counter1 : 2
counter1.decrement()   //Counter1 : 1
    
    
        
    

Here we make two calls to the makeCounter. Each call to makeCounter function to creates a new closure and returns the new counter object. Both the counters run independently of each other.

        
 
counter1 = makeCounter("Counter1")
counter2 = makeCounter("Counter2")
 
counter1.increment()   //Counter1 : 1
counter1.increment()   //Counter1 : 2
counter1.increment()   //Counter1 : 3
counter2.increment()   //Counter2 : 1
counter2.decrement()   //Counter2 : 0
counter2.increment()   //Counter2 : 1
counter2.increment()   //Counter2 : 2
counter1.decrement()   //Counter1 : 2
counter1.decrement()   //Counter1 : 1
    
    
        
    

Closures inside loops

In the following example, the makeFunctions creates an anonymous function and pushes it to the results array inside a For loop.

The code blocks ( { } ) in JavaScript creates new scope (block scope). Each iteration of the For loop creates a new scope and they become the parent scope of the anonymous function. Hence we end up with three anonymous functions each with the separate block scopes as their parent.

But all three block scopes share the makeFunctions as their parent scope as they are created inside the makeFunctions scope.

When you run executeFunctions you will see the output as 2, 2, 2 instead of 1, 2, 3.

        
function makeFunctions() {
 
  var result=[];
  for (let i = 0; i < 3; i++) {
    var item = i;
 
    //annonymous function is created here. Under the block scope of For Loop
    result.push( function() {console.log(item)} );
  }
  return result;
 
}
 
function executeFunctions() {
  var result = makeFunctions();
  
  for (var j = 0; j < result.length; j++) {
    //function invoked here
    result[j]();  
  }
}
 
 
executeFunctions();
 
//**** Result *****/
    
    
        
    

Unexpected results due to the fact that the variables declared with var will not become part of the block scope. It will become part of the functioUnexpected results due to the fact that the variables declared with var will not become part of the block scope. It will become part of the function scope or global scope. Hence in our example above item variable is part of the makeFunctions scope. our example above item variable is part of the makeFunctions scope.

So when the for iteration is complete, the value of the item is already 2 in the makeFunctions. When we run the anonymous functions they print that.

We can solve this problem using let instead of var, which is block scoped.

        
function makeFunctions() {
 
  var result=[];
  for (let i = 0; i < 3; i++) {
    let item = i;
 
    //annonymous function is created here. Under the scope of BuildList
    result.push( function() {console.log(item)} );
  }
  return result;
 
}
 
..
..
    
    
        
    

Closures in Callback

Event callbacks are one of the most commonly used use cases for closures. In the following code, we register an anonymous callback function. JavaScript creates a closure around it and allows us to access the clicked variable

        
        <!DOCTYPE html>
<html>
<head>
<title> Functions as callbacks </title>
<meta charset="utf-8"
</head>


<body>
<buuton>button id="buttonRef">Click Me </button>
</body>


    <script>
        let buttonRef = document.getElementById("buttonRef")
        let clicked=0
 
    buttonRef.addEventListener("click", function() {
        clicked++;
        buttonRef.innerHTML="Clicked "+clicked
    }, false);


    </script>