# 🌐JavaScript Scope & Execution: A Deep Dive into Hoisting, Closures, and Lexical Scoping🌐

JavaScript executes code in **distinct phases** while following to specific **scoping rules**. Understanding JavaScript’s scope is essential for writing **efficient, maintainable, and bug-free code**. In this blog, we will explore key concepts such as execution phases, different types of scope, and other important aspects that influence how JavaScript manages variables and function execution.

## **🔄 Phases of JavaScript Execution**

JavaScript code execution occurs in **three phases**:

1. **📜 Parsing Phase**

   - Converts the code into an **Abstract Syntax Tree (AST)**.
   - Breaks down each line of code into meaningful components.
   - Identifies syntax errors before execution.

2. **🔍 Compilation & Scope Resolution Phase**

   - Determines **variable/function scope**.
   - Sets up the **scope chain** for variable lookup.

3. **⚡ Execution Phase**
   - Runs code line-by-line.
   - Assigns values and executes functions.

---

## **🏗️ Execution Context & Scopes**

An **execution context** is an abstract concept that holds information about the environment within which the JavaScript code is executed. Every time a script or function runs, an execution context is created, determining which variables, objects, and functions are accessible during the code's execution.

### **Types of Execution Contexts**

1. **🌍 Global Execution Context:**

   - This is the default or base context. When the JavaScript engine starts executing your code, it first creates the Global Execution Context.
   - Variables and functions that are not inside any function are placed in the Global Execution Context.
   - There is only one Global Execution Context in any JavaScript program.

2. **📞 Function Execution Context:**

   - Every time a function is called, a new Function Execution Context is created for that function.
   - It contains the function’s own local variables, arguments object, `this` keyword, and more.
   - A Function Execution Context is created whenever a function is invoked and can be nested within other execution contexts.

3. **⚠️ Eval Execution Context:**

   - Created when `eval()` is called.
   - Generally avoided due to performance and security concerns.

### **Two Stages of Execution Context**

1. **🛠️ Creation Phase**

   - Memory is allocated for variables and functions, the scope chain is established, and the `this` keyword is defined (not in arrow functions).
   - Hoisting occurs, meaning variables declared with `var` are set to `undefined`, and function declarations are loaded into memory.

2. **🎬 Execution Phase**

   - The code is executed line by line.
   - Values are assigned to variables, and functions are executed.

---

## **🔎 Scopes in JavaScript**

Scopes determine where variables or functions are organized and accessible in the code. JavaScript's scoping mechanism is different from other languages like Java, C++, and Python, so avoid mixing these concepts.

### Types of Scope in JavaScript

1. **Global Scope**
2. **Function Scope**
3. **Block Scope**

### **1. 🌍 Global Scope**

Variables declared outside any function or block are in the global scope. These variables are accessible from anywhere in your code, whether inside a function, loop, or conditional statement.

**Example:**

```javascript
let country = 'India';

function showCountry() {
  console.log(country); // ✅ Accessible here
}

showCountry(); // Outputs: India

console.log(country); // ✅ Accessible here too
```

In the above code, `country` is declared in the global scope, so it can be accessed both inside and outside the `showCountry` function.

### **2. 📦 Function Scope**

When a variable is declared inside a function, it is scoped to that function, meaning it can only be accessed within that function.

**Example:**

```javascript
function showCity() {
  let city = 'Mumbai';
  console.log(city); // ✅ Accessible here
}

showCity(); // Outputs: Mumbai

console.log(city); // ❌ Error: city is not defined
```

Here, `city` is declared inside the `showCity` function, so it is only accessible within that function and not outside.

### **3. 🧱 Block Scope (ES6+)**

Block scope is created when variables are declared inside a pair of curly braces `{}`. This can happen within loops, conditionals, or just standalone blocks.

**Example:**

```javascript
if (true) {
  let festival = 'Diwali';
  console.log(festival); // ✅ Accessible here
}

console.log(festival); // ❌ Error: festival is not defined
```

In this example, `festival` is declared inside an `if` block, so it is only accessible within that block.

---

## **📜Variable Declarations and Scope**

In JavaScript, variables can be declared using `var`, `let`, or `const`. The way a variable is declared affects its scope.

### Any variable is used only in two ways:

- **RHS (Right-Hand Side):** When we consume the variable.
- **LHS (Left-Hand Side):** When we assign a value or declare the variable.

### `var`, `let`, and `const`

#### `var`

Variables declared with `var` are either function-scoped or globally scoped. `var` does not support block scope.

**Example:**

```javascript
function showVar() {
  if (true) {
    var festival = 'Diwali';
  }
  console.log(festival); // Accessible here, even though it's inside a block
}

showVar(); // Outputs: "Diwali"
```

**Another Example:**

```javascript
var city = 'Mumbai';
console.log(city, state); // Output: Mumbai undefined

if (true) {
  var city = 'Delhi';
  var state = 'Maharashtra';
  console.log(city, state); // Output: Delhi Maharashtra
}

console.log(city, state); // Output: Delhi Maharashtra
```

In the above example, during the scope resolution phase, both `city` and `state` are declared in the global scope due to the use of `var`. When the execution phase starts:

- `city` has been initialized to `Mumbai`.
- `state` is declared in the global scope but has not been assigned a value yet, so it is `undefined`.

Within the `if` block, `city` and `state` are reassigned:

- `city` is updated to `Delhi`.
- `state` is assigned the value `Maharashtra`.

Since `var` has function or global scope, the changes to `city` and `state` in the `if` block affect their values globally. Thus, when the final `console.log(city, state);` is executed, both `city` and `state` have the values `Delhi` and `Maharashtra`, respectively.

| **Note:** _How is function scope different from block scope?_

A variable with function scope has a unique characteristic: it can be defined anywhere within the function but will still be accessible throughout the entire function.

```javascript
function electionYear() {
  console.log('Upcoming election year is', year); // undefined
  var year = 2024;
  console.log('Upcoming election year is', year); // 2024
}
```

If we try to do the same thing using `let` instead of `var`, it will result in an error because `let` does not have function scope like `var` does.

| **Note:** _Automatically Global:_  
This refers to variables that are automatically added to the global scope in certain situations. Typically, this occurs when you create a variable inside a function without using the `var`, `let`, or `const` keyword. These variables become global, even if they are defined inside a function.

```javascript
var primeMinister = 'Atal Bihari Vajpayee'; // Declared globally

function updatePM() {
  primeMinister = 'Narendra Modi'; // Modifies the global variable
  currency = 'Rupee'; // 'currency' is not declared with var/let/const, so it becomes a global variable
}

console.log('Current Prime Minister:', primeMinister); // Atal Bihari Vajpayee
// console.log("Currency:", currency); // Error (not yet defined)

updatePM(); // Call function to modify `primeMinister` and declare `currency`
console.log('Current Prime Minister:', primeMinister); // Narendra Modi
console.log('Currency:', currency); // Rupee
```

Automatic global variables can cause issues in JavaScript. To prevent this, you can enable **strict mode** by adding the following at the top of your script:

```javascript
'use strict';
```

#### `let` and `const`:

These keywords support block scope, meaning variables declared inside a block are only accessible within that block.

**Example:**

```javascript
function showLetConst() {
  if (true) {
    let state = 'West Bengal';
    const capital = 'Kolkata';
    console.log(state, capital); // Accessible inside the block
  }
  console.log(state, capital); // Error: `state` and `capital` are not defined outside the block
}

showLetConst();
```

##### Special Characteristics of `let` and `const`

- **Block Scope Inside Functions**: Variables declared with `let` or `const` are not \[hoisted\](## Hoisting) in the same way as `var`. They are only accessible after their declaration, which differs from the function scope provided by `var`. This difference leads to the concept of the **Temporal Dead Zone (TDZ)**.

**Example:**

```javascript
function checkLet() {
  console.log(festival); // Error: Cannot access 'festival' before initialization
  let festival = 'Holi';
}

checkLet();
```

Here, `festival` is in the TDZ from the start of the block until the declaration is encountered, making it inaccessible before its declaration.

- **Temporal Dead Zone (TDZ)**: This is the region of the block before the variable is declared. A variable declared with `let`, `const`, or `class` is in the TDZ from the start of the block until the code execution reaches its declaration.
- **No Redeclaration**: Variables declared with `let` and `const` cannot be redeclared in the same scope. This is enforced during the first phase (compilation phase) of the JavaScript execution process.

**Example:**

```javascript
let independenceYear = 1947;
let independenceYear = 1857; // Error: Identifier 'independenceYear' has already been declared
```

---

## **👥 Variable Shadowing & Illegal Shadowing**

### **Variable Shadowing**

Variable shadowing happens when a variable in an inner scope (inside `{}`) has the same name as a variable in an outer scope. The inner variable "shadows" the outer one within its block.

**Example:**

```javascript
function showCapital() {
  let capital = 'Delhi'; // Outer scope
  if (true) {
    let capital = 'Mumbai'; // Inner scope shadows the outer variable
    console.log('Inside block:', capital); // Prints: Mumbai
  }
  console.log('Outside block:', capital); // Prints: Delhi (outer variable remains unchanged)
}

showCapital();
```

### **Illegal Shadowing**

Illegal shadowing happens when we try to shadow a `let` variable using `var`, which JavaScript does not allow. The reason is that `var` is function-scoped, while `let` is block-scoped. If `var` is declared inside a block, it "escapes" the block due to its function scope, causing a conflict with the existing `let` variable.

```javascript
function festivalSeason() {
  let festival = 'Diwali';
  if (true) {
    var festival = 'Holi'; // ❌ Not allowed! 'var' cannot shadow 'let'
    console.log(festival);
  }
}
festivalSeason();
```

🔴 **Error:** `SyntaxError: Identifier 'festival' has already been declared`

---

## **⛓️ Lexical Scoping / Lexical Parsing**

JavaScript uses lexical scoping, also known as static scoping. In lexical scoping, the scope of variables is determined during compile time. Although variable values are assigned during the execution phase, the scope of each variable is defined during the compilation phase. Therefore, the rules for accessing variables are based on the location where functions and blocks are written in the code.

| **Scope Chaining**: Scope chaining is the process by which JavaScript looks up variable values in the current scope and, if not found, continues searching in the outer scopes, following the chain of scopes until the variable is found or the global scope is reached. This is possible because of lexical scoping, where nested functions have access to variables in their outer functions.

**Example:**

```javascript
function outerFunction() {
  let country = 'India';

  function innerFunction() {
    console.log(country); // Accessible due to lexical scoping
  }

  innerFunction();
}

outerFunction(); // Outputs: India
```

In the above code, `innerFunction` can access `country` because it is lexically within the scope of `outerFunction`. If `country` were not found in the immediate scope of `innerFunction`, JavaScript would check the next outer scope (in this case, `outerFunction`'s scope) to find it, forming a chain of scopes.

In summary, **scope chaining** allows JavaScript to search through the chain of scopes to find variables, starting from the innermost scope and moving outward. This behavior is a fundamental aspect of lexical scoping.

---

## **🔗 Closures: Preserving Scope**

A **closure** provides access to the **reference** of variables from its parent function, even after that parent function has returned. The function keeps a reference to its outer scope, preserving the scope chain over time. This means that the variable environment of the execution context in which the function was created remains accessible even after that execution context has finished.

**Example:**

```javascript
const bookTrainTicket = function () {
  let ticketsBooked = 0;
  return function () {
    ticketsBooked++;
    console.log(`🚆 ${ticketsBooked} ticket(s) booked for Indian Railways`);
  };
};

const ticketCounter = bookTrainTicket();

ticketCounter(); // 🚆 1 ticket(s) booked for Indian Railways
ticketCounter(); // 🚆 2 ticket(s) booked for Indian Railways
ticketCounter(); // 🚆 3 ticket(s) booked for Indian Railways
```

This example demonstrates how the inner function retains access to `ticketsBooked`, even after `bookTrainTicket` has completed its execution.

### Closures Store References, Not Values

A closure does not store the variable value but a **reference** to the variable. So, if the variable value changes before the function is returned, the updated value will be used at the time of execution.

**Example:**

```javascript
const planWedding = function () {
  let guestsCount = 150; // Initial guest count for the wedding
  function displayGuestCount() {
    console.log(`${guestsCount} guests invited for the wedding.`);
  }
  // After adding more guests
  guestsCount += 50;
  return displayGuestCount;
};

const finalGuestList = planWedding();
finalGuestList(); // 200 guests invited for the wedding.
```

### Closures Can Access Variables from Multiple Levels Up

A closure can access not only its immediate parent’s variables but also variables from **higher-level parent functions**.

**Example:**

```javascript
function sweetShop() {
  let totalSweets = 100; // Initial number of sweets in the shop

  // Function to handle sweet orders
  const orderSweets = function (sweetsOrdered) {
    totalSweets -= sweetsOrdered; // Reduce the sweets in stock by the ordered amount

    function displayStock() {
      console.log(`${totalSweets} sweets left in the shop.`);
    }

    // Call displayStock to show the updated sweets count
    displayStock();
  };

  return orderSweets;
}

const shop = sweetShop(); // Create the sweet shop

// Simulating some orders
shop(20); // 80 sweets left in the shop.
shop(10); // 70 sweets left in the shop.
```

---

## **🔼 Hoisting: Behind the Scenes**

**Hoisting** is a term commonly used in the JavaScript community, although it is not officially defined in the ECMAScript specification. It refers to a behavior in JavaScript's scoping mechanism, which occurs in two main phases of code execution: the **Compilation and Scope Resolution Phase** and the **Interpretation or Execution Phase**. During the compilation phase, many variables are already identified, so when the code is executed, it seems as JavaScript is aware of these variables or functions even before their actual declaration. This phenomenon, where the interpreter appears to move the declarations of functions, variables, classes, or imports to the top of their scope before execution, is known as **hoisting**.

- **Example of Hoisting with** `var` and `let`:

```javascript
function hoistExample() {
  console.log(city); // undefined (var is hoisted and initialized with undefined)
  // console.log(state); // ❌ ReferenceError (let is in the Temporal Dead Zone)
  var city = 'Kolkata';
  let state = 'West Bengal';
  console.log(city); // Kolkata
  console.log(state); // West Bengal
}

hoistExample();
```

In this example:

- `var city` is hoisted, so `console.log(city);` outputs `undefined` because its value is not assigned until the line `var city = "Kolkata";`.
- `let state` is also hoisted, but it is in the **Temporal Dead Zone (TDZ)** from the start of the block until the declaration is encountered. Accessing `state` before its declaration throws a **ReferenceError** because variables declared with `let` are not initialized until the execution reaches the `let` statement.
- **Example of Hoisting with a** `Function Declaration`:

```javascript
console.log(getTrainFare(500, 2)); // 1000 Rupees

function getTrainFare(price, passengers) {
  return `${price * passengers} Rupees`;
}
```

In this example, the function `getTrainFare` is fully hoisted, allowing it to be called before its declaration in the code.

- **Hoisting with** `Function Expressions`:

If a function expression is created using `const` or `let`, the function itself is not available before its declaration because the variable is hoisted but remains in the **Temporal Dead Zone** (TDZ). If a function expression is created using `var`, it is hoisted but initialized with `undefined`. If you try to call the function before its definition, it will throw a `TypeError`, stating that the function is `not a function`, because the code attempts to call `undefined()`.

```javascript
console.log(bookFlight(3)); // TypeError: bookFlight is not a function

var bookFlight = function (seats) {
  return `✈️ ${seats} seat(s) booked for Air India`;
};
```

---

## **🎯 Final Thoughts**

✔️ **Scope Types**: Global, Function, Block (`let`/`const`).  
✔️ **Hoisting**: `var` (initialized), `let`/`const` (TDZ), functions (fully hoisted).  
✔️ **Closures**: Functions retaining parent scope access.  
✔️ **Shadowing**: Inner variables override outer ones (but `var` can’t shadow `let`).

Master these concepts to write **predictable, scalable JavaScript**!

Did this help? Share it with a fellow code-speedrunner! 🏁💻
Questions? Ask anything below! 🚗💨

**Happy Coding!** 💻🚀
