Lexical Scoping πŸ”­, Scope Chaining πŸ”—,  and Closures 🌌 In JavaScript

Lexical Scoping πŸ”­, Scope Chaining πŸ”—, and Closures 🌌 In JavaScript

Β·

7 min read

Hi, in this article we will understand some of the most misunderstood, confusing yet interview-important topics like Lexical Scoping and Closures in JavaScript. After searching on the internet for a very long, watching multiple YouTube videos, if you still haven't understood Closures, then I think this is the article for you.

But, before starting with these topics, first, we need to understand what scope is in JavaScript.

Scope πŸ‘€

There are two types of scope:

  1. Global Scope 🌍
  2. Local Scope πŸŒ†

image.png

Whenever you define something, like a variable, you want it to be inside certain boundaries because sometimes it does not make sense to have a variable outside a function. For example, if you have a function that calculates the area of a rectangle using two inputs height and width, and returns the area, then you need the variable area inside the function only and there is no need for the area outside the function.

let height = 20;
let width = 10;

function calculateArea() {
  let area = height * width;
  console.log(area);  // area = 200
}

calculateArea();

This function will print the area, as expected.

But, what if we want to print the area again, we can just directly do console.log(area) because we already have the area of the rectangle inside the area variable, right? Let's see.

let height = 20;
let width = 10;

function calculateArea() {
  let area = height * width;
  console.log(area);  // area = 200
}

calculateArea();

console.log(area);  // ReferenceError: area is not defined

But this gave an error, did we do something wrong? No !!!, this is how the scope in JavaScript works. This accessibility of elements in JavaScript is known as Scope.

Scope in JavaScript is determined by the function or the block of code wrapped inside { }.

For the above example, the scope of the area variable was only inside the function calculateArea(), outside the function area is inaccessible.

The scope is like till where an element is relevant, variable area is relevant only in the function calculateArea(), thus its scope is inside function calculateArea() only and not outside it.

image.png

Because the scope is limited to the function only, we can define the same variable(area) inside multiple functions and it will not cause an issue. Let's try that.

image.png

Even though area was defined at two different places, still it did not give any error because the scope of the first area was only inside the function calculateArea() and the second area only had scope inside the function calculateAreaSquare().

Now, let's take a look at Nested Scope.

Nested Scope πŸ‘¨β€πŸ‘©β€πŸ‘¦

When we have one scope inside another scope it creates a nested scope.

function outerFunction() {
  let outerVariable = "I am outer function";

  function innerFunction() {
    let innerVariable = "I am Inner Variable";
    console.log(outerVariable + " and " + innerVariable);
  }

  innerFunction();
}

outerFunction(); // "I am outer function and I am Inner Variable"

image.png

Inner Scope can access the variables of the Outer Scope but the same is not possible vice-versa. It is similar to how a living being inherits properties from their parents. A child inherits from parents but parents do not inherit from the child.

Similar to that, in the above example we can see that outerVariable is inside function outerFunction() (parent function) but still outerVariable is accessible inside function innerFunction() (child function)

Thus we can conclude two things,

  1. Scopes can be nested
  2. Elements of the outer scope are accessible inside the inner scope but not vice-versa

Now, as we have understood the scope and nested scope, let's start with Lexical Scoping.

Lexical Scope πŸŽ‚

In simple terms, Lexical scope is the place where the item got created.

Example.

let myName = "Dan";

function call() {
  console.log(myName);
}

call();

In the above example variable myName is called inside function call() i.e. local scope, but as we know Lexical Scope is the place where the item got created, not called, and in this example, the variable was initialized in Global Scope, thus the Lexical scope of variable myName is Global Scope.

Let's look at another example:

function call() {
  let myName = "Dan";
  console.log(myName);
}

call();

Here Lexical Scope of myName is call() functional's local scope because myName is created inside the function call().

Thus we can say that Lexical Scope determines the access to the elements in different scopes.

Scope Chaining πŸ”—

So, as we have seen Lexical Scoping decides the accessibility of variables, and that process is known as scope chain.

Scope Chain means nothing but looking for the required variable first in the local scope and if JavaScript engine does not find it there then it looks for it in the outer scope. Like how qualities are inherited from grandparents to parents and from parents to children. Similarly, a function can access elements of its parent scope a global or local/functional scope and that parent function can access the elements of its parent scope global or functional/local scope.

Let's understand this by an example.

// global scope
let globalVariable = "Global";

// outer function scope
function parent() {
  let parentVariable = "Parent variable";

  // inner function scope
  function child() {
    let childVariable = "Child Variable";

    console.log(`I am ${globalVariable}, I am ${parentVariable} and I am ${childVariable}`);
  }
  child();
}
parent();

image.png

Here when the child() function runs, it looks for globalVariable, and as it is not in the local scope Js Engine searches for it in the outer scope i.e. the parent() function scope but globalVariable is not present there also thus Js Engine goes to the globalScope where the variable is present.

We can see a chain is formed from child() -> parent() -> global. This is how a scope chain is formedπŸ‘Ά---> πŸ™β€β™€οΈ --->🌎

Now let's understand what Closure is.

Closures 🌌

The easiest explanation I have read about Closure is.

A closure is a function having access to the parent scope, even after the parent function has closed.

The first part A closure is a function having access to the parent scope, this is lexical scope, but the second part is what make closures unique.

...even after the parent function has closed.

let's take an example.

let a = 10;

function number() {
  let b = 20;

  function add() {
    let c = 20;
    let d = a + b + c;
    console.log(d);
  }

  return add;
}

let ref1 = number();
ref1(); // logs 50

So what just happened here, even after the outer function completed the execution, how come we got the value from the inner function? This is what closure is.

Here, in the above example when we call the outer function number() it returns the reference of inner function add(), but we can it does not executes the inner function it just stores its reference in the variable ref1. To execute the inner function we give () after the variable name i.e ref1(), this executes the inner function. So even after the outer function is executed and the scope of variable b is inside the outer function, we still are able to call the variable b in the inner function add() which is referred as ref1.

In other words, closure is created when a child function keeps the environment of the parent scope even after the parent function has already been executed.

Let's take look at another example.

const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

counter.increment();
counter.increment();
counter.increment();
console.log(counter.value()); // 3

Here variable counter is storing a function that has a variable privateCounter and it returns functions increment(), decrement() and value().

counter is an immediately invoked function because it is executed by () just after it is created.

Now as function is an object in JavaScript, we can call the inner function by giving . i.e counter.increment() this will execute the increment function which will execute changeBy function and this changeBy function increases the value of variable privateCounter which is not accessible from outside the function but due to closures we are able to access privateCounter variable.

And finally when counter.value() is called it returns the value of privateCounter.

This is how closures work in JavaScript.

A closure is a function having access to the parent scope, even after the parent function has closed.

We learned about the following topics in this article:

  • Scope
  • Nested Scope
  • Lexical Scope
  • Scope Chaining
  • Closures

Hope you find this article helpful. We will be understanding more concepts of JavaScript in further articles.

Thank you for reading !!! πŸ˜ƒ

Β