🚀How JavaScript Works (Part 4)? Hoisting, var, let, const, TDZ
Tags: webdev, javascript, programming, tutorial
Published: 2023-10-25 18:05:47

JavaScript, often perceived as an interpreted language due to its runtime execution, is, in fact, a language with a **two-step** process, including a critical [compilation stage](https://dev.to/samanabbasi/how-javascript-works-part-1-look-at-js-engine-1kkb). The compilation process comprises **tokenization/lexing**, **parsing**, and actual code compilation, making JavaScript more than just a simple scripting language. This compilation stage is essential to understand the concept of **hoisting**.

## Table of Contents
- [The Compilation Stage](#the-compilation-stage)
- [The Role of Parsing, AST, and Scope Analysis for Hoisting](#the-role-of-parsing-ast-and-scope-analysis-for-hoisting)
- [How Does Hoisting Fit into This Process?](#how-does-hoisting-fit-into-this-process)
- [Hoisting with Variable Declarations](#hoisting-with-variable-declarations)
- [Hoisting with Function Declarations](#hoisting-with-function-declarations)
- [Variable Declarations and Function Declarations](#variable-declarations-and-function-declarations)
  - [Function Declarations](#function-declarations)
  - [Variable Declarations](#variable-declarations)
- [`let` Doesn't Hoist?](#let-doesnt-hoist)
- [Temporal Dead Zone and **TDZ** Error](#temporal-dead-zone-and-tdz-error)
- [**TDZ** and `const`](#tdz-and-const)
- [Difference Between `var` and `let`/`const` Hoisting](#var-let-const)
- [Function Expressions](#function-expressions)
- [Conclusion](#conclusion)
- [Sources](#sources)

## The Compilation Stage

Before a JavaScript program executes, it goes through a series of phases during the [compilation stage](https://dev.to/samanabbasi/how-javascript-works-part-1-look-at-js-engine-1kkb):

- **Tokenization/Lexing:**
 The engine breaks the source code into chunks called `tokens`. Tokens are the smallest units of a program, like words in natural language.

- **Parsing:** The engine analyzes the syntax of the `token` stream to create an abstract syntax tree (`AST`). The `AST` represents the grammatical structure of the code and helps the engine understand its meaning.

- **Compilation:** During this phase, the engine translates the `AST` into executable code, often in the form of bytecode or machine code. This compiled code is optimized for execution.

## The Role of Parsing, AST, and Scope Analysis for Hoisting

When a JavaScript program is executed, the first crucial step is the parsing of the source code. Parsing is the process of taking raw JavaScript code and transforming it into an Abstract Syntax Tree (`AST`). This tree-like structure represents the grammatical and structural elements of the code.

The `AST`, in combination with **[scope analysis](https://dev.to/samanabbasi/how-javascript-works-part-2-the-lexical-scope-mechanism-of-javascript-52jb)**, plays a pivotal role in the process of **hoisting**.

> **Scope analysis involves identifying where in the code certain variables and functions are valid or accessible. It is during this analysis that JavaScript engines determine the boundaries of various scopes in your code.**

## How Does Hoisting Fit into This Process?

**Hoisting** is not some mystical mechanism but rather a direct consequence of this parsing and scope analysis. It is essentially the act of re-arranging variable and function declarations and attaching them to the top of their appropriate scopes.

Let's break it down:

- **Variable Declarations:**
 When you declare a variable using `var`, for instance, the JavaScript engine identifies it during the **parsing phase**. It then hoists this declaration to the top of the current scope, ensuring that the variable can be accessed anywhere within that scope. This means that you can use a variable before it's declared in your code without causing an error.

- **Function Declarations:** Similarly, `function` declarations are also hoisted. The engine recognizes them during parsing, moves them to the top of the current scope, and assigns a reference to the `function`. This allows you to call a `function` before it's formally defined in your code.

By performing this hoisting process based on the information gathered from parsing and scope analysis, JavaScript ensures that variables and functions are accessible where they should be. This is why hoisting is often described as just a matter of re-arranging these declarations to the top of their appropriate scopes.

## Hoisting with Variable Declarations

Let's start with variable declarations. Consider this code:

```javascript
console.log(a); // Outputs: undefined
var a = 5;
console.log(a); // Outputs: 5
```
The magic happens here! Despite trying to `console.log` the variable `a` before it's declared, JavaScript doesn't throw an error. Instead, it hoists the variable declaration to the top of the current scope, making it available but `uninitialized`. The code is effectively treated as if written like this:

```javascript
var a; // Declaration is hoisted
console.log(a); // Outputs: undefined
a = 5; // Initialization still happens where you originally defined it
console.log(a); // Outputs: 5
```

## Hoisting with Function Declarations

Now, let's see how function declarations are hoisted:

```javascript
sayHello(); // Outputs: "Hello, World!"
function sayHello() {
  console.log("Hello, World!");
}
```
In this case, we're calling the `sayHello` function before its actual declaration. JavaScript hoists the function declaration to the top of the current scope, making it accessible before its appearance in the code. The code behaves as if it were written like this:

```javascript
function sayHello() {
  console.log("Hello, World!");
}
sayHello(); // Outputs: "Hello, World!"
```
This demonstrates how `function` declarations are hoisted in JavaScript, allowing them to be used before they are formally defined in the code.

## Variable Declarations and Function Declarations

To grasp the nuances of hoisting, it's essential to differentiate between variable declarations and function declarations. The behavior differs significantly between these two constructs.

### Function Declarations

**Function declarations are hoisted entirely, encompassing both the declaration and initialization phases**. This means that when a function is declared using the `function` keyword, it is effectively recognized as a function declaration. As a unique feature, function declarations don't require an assignment operator (`=`) since the JavaScript engine comprehends that they are meant to create a function. This hoisting behavior of function declarations aligns with the rules set forth by the ECMAScript language standard.

The rationale behind hoisting function declarations in their entirety is grounded in the behavior specified in the ECMAScript standard. In JavaScript, functions are considered **"first-class citizens,"** signifying that they are treated as objects and can be referenced much like any other variable or value. 

**The Benefits of Function Hoisting:**

This full hoisting enables the calling of a function before its formal declaration within the code, fostering flexibility in code organization. It also contributes to more readable code, particularly in situations where functions invoke one another.

This consistency in hoisting function declarations is a fundamental aspect of JavaScript's design and execution model. It ensures reliable and predictable behavior across various JavaScript engines and environments, aligning with the ECMAScript language standard.

### Variable Declarations

On the other hand, variable declarations exhibit a contrasting hoisting behavior. While they are indeed hoisted, only the declaration part is elevated to the top of the current scope. The initialization of variables, however, remains at its original position in the code. This variance in hoisting behavior distinguishes variable declarations from function declarations.

## `let` Doesn't Hoist? <a name="let-doesnt-hoist"></a>

Contrary to common belief, `let` does hoist, but it exhibits a distinct behavior compared to `var`. In the case of `var`, variables are hoisted to the entire function scope and are initialized to `undefined` at the start of the function's execution. However, when it comes to `let`, it does hoist, but it doesn't get `initialized` during the hoisting phase. Instead, it enters what is known as the Temporal Dead Zone (`TDZ`).

## Temporal Dead Zone and **TDZ** Error

The Temporal Dead Zone (`TDZ`) is a state where `let` and `const` variables exist but remain `uninitialized`. If you attempt to access a `let` variable before it's explicitly initialized in your code, JavaScript will throw a `TDZ` error. This error is a safeguard to ensure that you don't access variables in an unpredictable or incomplete state.
```javascript
console.log(a); // Throws a ReferenceError: Cannot access 'a' // before initialization
let a = 5;
```
## **TDZ** and `const` <a name="tdz-and-const"></a>

The concept of `TDZ` primarily arose because of the behavior of `const` declarations. If `const` variables were allowed to be accessed before they were assigned a value, it would contradict the core principle of `const`, which promises immutability and a single assigned value throughout a variable's lifetime. To prevent this, the `TDZ` mechanism was introduced.

## Difference Between `var` and `let`/`const` Hoisting <a name="var-let-const"></a>

The critical difference in hoisting behavior is that `var` variables are initialized to `undefined` during hoisting. This means that you can access them right away, even if their assignment happens later in the code. On the other hand, `let` and `const` variables don't receive an initial value during hoisting. They enter the `TDZ`, and the only way to bring them out of this state is to explicitly initialize them using the `let` or `const` keyword within the block scope. This behavior ensures a clear and consistent approach to variable initialization in JavaScript.

The `const` keyword works similarly to `let` in terms of block scoping but with one crucial difference: it declares variables that cannot be reassigned after they are initialized. This can be particularly useful when you want to ensure that a variable retains its value within a block.

## Function Expressions

In contrast, function expressions do not exhibit the same behavior. They are not hoisted in the same way as function declarations. When a function is defined through an expression (typically by assigning it to a variable), it's a **runtime operation**, not something handled during the compilation phase. This means that the variable associated with a function expression is indeed hoisted, but the assignment, which gives it a function value, is not hoisted. Therefore, attempting to call a function expression before it is assigned will result in a `TypeError`.
```javascript
sayHello(); // Throws a TypeError
var sayHello = function () {
  console.log("Hello, World!");
};
```
The reason for this `TypeError` is straightforward: **you're trying to execute a variable that, at that point in the code, holds the value `undefined`**. In contrast, with function declarations, the engine has a plan to initialize them to `undefined` during compilation, which provides a safety net for calling them before their actual declaration.

In essence, hoisting in JavaScript, whether for function declarations or expressions, is about ensuring that functions are available throughout their scope. However, the way they are initialized and made accessible varies between declarations and expressions, leading to differences in behavior and potential errors like `TypeError` when calling an `undefined` function expression.

## Conclusion
Understanding hoisting in JavaScript is essential for writing reliable and predictable code. Hoisting, facilitated by the parsing and scope analysis process, ensures that variables and functions are accessible where they should be. It plays a crucial role in differentiating between variable and function declarations, the behavior of `let` and `const`, and the handling of function expressions. This knowledge empowers developers to write clean, organized, and error-free JavaScript code, embracing the intricate, yet powerful, mechanism of hoisting.

## Sources
**Kyle Simpson's "You Don't Know JS"**
**MDN** Web Docs - The Mozilla Developer Network (MDN)