Hoisting, Lexical Scope, and Temporal Dead Zone in JavaScript
Tags: webdev, javascript, react, beginners
Published: 2024-07-01 06:56:11

JavaScript is often a quirky language. It's crucial to understand concepts like **hoisting, lexical scope, and the temporal dead zone (TDZ)**. Rather than just going through theory, let's dive into practical examples that illustrate these concepts, some of which might initially seem confusing.

## Hoisting
Hoisting is JavaScript's behavior of moving declarations to the top of their containing scope during compilation. This can lead to some unexpected results.

### Example 1: Variable Hoisting
```
console.log(myVar); // undefined
var myVar = 5;
console.log(myVar); // 5
```
In this example, you might expect a ReferenceError for the first console.log, but JavaScript "hoists" the declaration of In this example, you might expect a ReferenceError for the first console.log, but JavaScript "hoists" the declaration of myVar to the top, so it exists (but is undefined) before its initialisation. to the top, so it exists (but is undefined) before its initialisation.

### Example 2: Function Hoisting
```
console.log(myFunction()); // "Hello, world!"

function myFunction() {
  return "Hello, world!";
}
```
Function declarations are also hoisted, allowing them to be called before their definition in the code.

### Example 3: Hoisting with Function Expressions
When a function is assigned to a variable using var, only the variable declaration is hoisted, not the function assignment. This can lead to different behavior compared to function declarations.
```
console.log(myFunction); // undefined
myFunction(); // TypeError: myFunction is not a function

var myFunction = function() {
  console.log("Hello, world!");
};

myFunction(); // "Hello, world!"
```
The variable myFunction is hoisted and initialised to undefined, causing a TypeError when called before the function assignment.

### Example 4: Hoisting Confusion with Var
```
function testHoisting() {
  console.log(foo); // undefined
  var foo = 'bar';
  console.log(foo); // "bar"
}

testHoisting();
```

In testHoisting, foo is hoisted and initialised to undefined, resulting in the first console.log(foo) logging undefined.

### Example 5: Hoisting with Let and Const
```
function testLetConst() {
  console.log(bar); // ReferenceError
  let bar = 'baz';
  console.log(bar); // "baz"
}

testLetConst();
```
Variables declared with let and const are not hoisted to the top, so the first console.log(bar) results in a ReferenceError.

---

## Lexical Scope
Lexical scope means that the scope of a variable is determined by its position in the source code.

### Example 1: Nested Functions (Lexical Scope)
```
function outerFunction() {
  var outerVar = 'I am outside!';

  function innerFunction() {
    console.log(outerVar); // "I am outside!"
  }

  innerFunction();
}

outerFunction();
```
Here, **innerFunction** can access **outerVar** because it is defined within the same lexical scope.

### Example 2: Scope Confusion (Lexical Scope)
```
var x = 10;

function foo() {
  var x = 20;
  bar();
}

function bar() {
  console.log(x); // 10
}

foo();
```
One might expect bar() to print 20, but it prints 10 because bar is defined in the global scope, where x is 10.

### Example 3: Lexical Scope and Closures (Lexical Scope)
```
function createFunction() {
  var localVar = 'local';

  return function() {
    console.log(localVar); // "local"
  };
}

var myFunction = createFunction();
myFunction();
```
myFunction retains access to localVar even after createFunction has finished executing, demonstrating a closure

### Example 4: Lexical Scope in Loops (Lexical Scope)
```
for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); // 3, 3, 3
  }, 1000);
}

for (let j = 0; j < 3; j++) {
  setTimeout(function() {
    console.log(j); // 0, 1, 2
  }, 1000);
}
```
The var declaration in the first loop results in i being hoisted to the global scope, so all three setTimeout callbacks log 3. The let declaration in the second loop creates a new j for each iteration, so the callbacks log 0, 1, and 2.

### Example 5: Comparing Lexical Scope for Function Declarations and Arrow Functions
I think this topic is big enough to complete in one example, so i created a article about it, [here is the link](https://dev.to/rahulvijayvergiya/comparing-lexical-scope-for-function-declarations-and-arrow-functions-go3)


---
## Temporal Dead Zone (TDZ)
The TDZ refers to the period between the entering of a scope and the actual declaration of variables, where they cannot be accessed.

### Example 1: Let and Const - Temporal Dead Zone (TDZ)
```
console.log(a); // ReferenceError
let a = 5;
```
Unlike var, variables declared with let and const are not hoisted to the top of their block scope, resulting in a ReferenceError if accessed before their declaration.

### Example 2 - Temporal Dead Zone (TDZ)
```
{
  console.log(b); // ReferenceError
  let b = 10;
}

{
  let c = 20;
  console.log(c); // 20
}
```
In the first block, accessing b before its declaration results in a ReferenceError due to the TDZ. In the second block, c is accessed after its declaration, so it logs 20.

### Example 3: Temporal Dead Zone with Functions

```
{
  console.log(func); // ReferenceError
  const func = function() {
    return 'Hello!';
  };
}

{
  const func = function() {
    return 'Hello!';
  };
  console.log(func()); // "Hello!"
}
```
In the first block, accessing func before its declaration results in a ReferenceError due to the TDZ. In the second block, func is accessed after its declaration, so it works as expected.

## Combining Concepts

### Example 1: Hoisting and TDZ
```
{
  console.log(d); // ReferenceError
  let d = 15;
  console.log(d); // 15
}
```
This example shows how accessing a let variable before its declaration results in a ReferenceError due to the TDZ, even though the declaration is hoisted to the top of the block.

### Example 2: Lexical Scope and Hoisting
```
var e = 30;

function test() {
  console.log(e); // undefined
  var e = 40;
  console.log(e); // 40
}

test();
console.log(e); // 30
```
In test(), e is hoisted and initialised to undefined within the function scope, so the first console.log(e) logs undefined.

### Example 3: Hoisting, Lexical Scope, and TDZ Combined
```
let f = 50;

function outer() {
  console.log(f); // 50
  let f = 60;
  inner();
  
  function inner() {
    console.log(f); // ReferenceError
  }
}

outer();
```
Here, inner is defined within the scope of outer, but f is in the TDZ within outer before its declaration, leading to a ReferenceError when inner tries to access it.

## Conclusion
Understanding **hoisting, lexical scope, and the temporal dead zone** is vital for writing robust JavaScript code. These examples illustrate how JavaScript handles variable and function declarations, scopes, and the strange behaviour of accessing variables before their declaration. By experimenting with these concepts, you'll develop a more intuitive grasp of JavaScript's behaviour.

**Related Article By Author:**
- [Node.js vs. Browser: Understanding the Global Scope Battle](https://dev.to/rahulvijayvergiya/nodejs-vs-browser-understanding-the-global-scope-battle-39al)
- [Comparing Lexical Scope for Function Declarations and Arrow Functions](https://dev.to/rahulvijayvergiya/comparing-lexical-scope-for-function-declarations-and-arrow-functions-go3)

References
[MDN Web Docs: Hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting)
[MDN Web Docs: Closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)
[MDN Web Docs: Lexical Scoping](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#lexical_scoping)
[MDN Web Docs: Temporal Dead Zone](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#temporal_dead_zone_tdz)
[JavaScript Info: Variable Scope, Closure](https://javascript.info/closure)
[Eloquent JavaScript: Functions](https://eloquentjavascript.net/03_functions.html)