READMEJavaScript

README

Module 12 Execution Context / .3 Hoisting Deep Dive

Concept Lesson
Advanced
4 min

Learning Objective

Understand Module 12 Execution Context well enough to explain it, recognize it in JavaScript, and apply it in a small task.

Why It Matters

This concept is part of the foundation that later lessons and projects assume you already understand.

ExecutionContextWhat Gets HoistedFunction Declaration HoistingWhat Javascript Sees
Private notes
0/8000

Notes stay private to your browser until account sync is configured.

README
2 min read18 headings

6.3 Hoisting Deep Dive

Introduction

Hoisting is JavaScript's behavior of moving declarations to the top of their scope during the creation phase. While it might seem magical, it's actually a predictable result of how execution contexts are created.


What Gets Hoisted?

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 HOISTING SUMMARY                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                         β”‚
β”‚  βœ… Function declarations    - Fully hoisted            β”‚
β”‚  βœ… var declarations         - Hoisted (as undefined)   β”‚
β”‚  ⚠️  let declarations        - Hoisted (but in TDZ)     β”‚
β”‚  ⚠️  const declarations      - Hoisted (but in TDZ)     β”‚
β”‚  βœ… Class declarations       - Hoisted (but in TDZ)     β”‚
β”‚  ❌ Function expressions     - NOT hoisted              β”‚
β”‚  ❌ Arrow functions          - NOT hoisted              β”‚
β”‚                                                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Function Declaration Hoisting

Function declarations are fully hoistedβ€”both the name and the implementation:

// This works!
sayHello(); // "Hello!"

function sayHello() {
  console.log('Hello!');
}

What JavaScript Sees:

// Creation phase
function sayHello() {
  console.log('Hello!');
}

// Execution phase
sayHello(); // "Hello!"

Multiple Functions:

// Both are available from the start
first(); // "First!"
second(); // "Second!"

function first() {
  console.log('First!');
}

function second() {
  console.log('Second!');
}

var Hoisting

Variables declared with var are hoisted, but only the declaration, not the initialization:

console.log(message); // undefined (not an error!)
var message = 'Hello';
console.log(message); // "Hello"

What JavaScript Sees:

// Creation phase
var message; // Declaration hoisted, initialized to undefined

// Execution phase
console.log(message); // undefined
message = 'Hello'; // Assignment happens here
console.log(message); // "Hello"

Common Mistake:

function example() {
  console.log(x); // undefined (not an error!)

  if (true) {
    var x = 10; // Hoisted to function scope!
  }

  console.log(x); // 10
}
example();

let and const Hoisting (Temporal Dead Zone)

let and const ARE hoisted, but they're in a "Temporal Dead Zone" (TDZ) until the declaration is reached:

// TDZ starts here for 'name'
console.log(name); // ReferenceError: Cannot access 'name' before initialization

let name = 'Alice'; // TDZ ends here

console.log(name); // "Alice"

Temporal Dead Zone Visualized:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  function example() {                                   β”‚
β”‚      // β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚      // β”‚     TEMPORAL DEAD ZONE for 'x'          β”‚    β”‚
β”‚      // β”‚                                          β”‚    β”‚
β”‚      // β”‚  console.log(x); // ReferenceError!      β”‚    β”‚
β”‚      // β”‚  doSomething();                          β”‚    β”‚
β”‚      // β”‚                                          β”‚    β”‚
β”‚      // β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚      let x = 10; // TDZ ends, 'x' is now accessible    β”‚
β”‚      console.log(x); // 10                              β”‚
β”‚  }                                                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Why TDZ Exists:

TDZ prevents the confusing behavior of var:

// With var (confusing)
console.log(x); // undefined - exists but no value
var x = 10;

// With let (clearer error)
console.log(y); // ReferenceError - clearly an error
let y = 10;

Function Expressions Are NOT Hoisted

sayHi(); // TypeError: sayHi is not a function

var sayHi = function () {
  console.log('Hi!');
};

What JavaScript Sees:

// Creation phase
var sayHi; // undefined

// Execution phase
sayHi(); // undefined() - TypeError!
sayHi = function () {
  console.log('Hi!');
};

With let/const:

sayHi(); // ReferenceError: Cannot access 'sayHi' before initialization

const sayHi = function () {
  console.log('Hi!');
};

Arrow Functions Are NOT Hoisted

Same as function expressions:

greet(); // ReferenceError (with const) or TypeError (with var)

const greet = () => {
  console.log('Hello!');
};

Hoisting Order of Precedence

When both a function and variable have the same name:

console.log(typeof foo); // "function"

var foo = "I'm a string";

function foo() {
  return "I'm a function";
}

console.log(typeof foo); // "string"

Rules:

  1. Function declarations are hoisted first
  2. Variable declarations are ignored if name already exists
  3. Variable assignments happen during execution

Step by Step:

// 1. Creation Phase
function foo() {
  return "I'm a function";
} // Function hoisted first
// var foo ignored - 'foo' already exists

// 2. Execution Phase
console.log(typeof foo); // "function"
foo = "I'm a string"; // Assignment overwrites
console.log(typeof foo); // "string"

Class Hoisting

Classes are hoisted but remain in the TDZ:

const instance = new MyClass(); // ReferenceError!

class MyClass {
  constructor() {
    this.name = 'MyClass';
  }
}

Class Expressions:

const instance = new MyClass(); // ReferenceError or TypeError

const MyClass = class {
  constructor() {
    this.name = 'MyClass';
  }
};

Hoisting in Blocks

var Ignores Blocks:

function example() {
  if (false) {
    var x = 10; // Still hoisted to function scope!
  }
  console.log(x); // undefined (not an error)
}
example();

let/const Respect Blocks:

function example() {
  if (false) {
    let x = 10; // Block-scoped
  }
  console.log(x); // ReferenceError: x is not defined
}
example();

Hoisting in Loops

The Classic var Loop Problem:

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3 (not 0, 1, 2!)

Why? var i is hoisted to function scope. There's only ONE i.

The let Fix:

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2

Why? let creates a new i for each iteration.


Best Practices

1. Declare Variables at the Top

// Good: Variables at top
function processUser(user) {
  const name = user.name;
  const age = user.age;
  let status = 'pending';

  // ... rest of function
}

// Avoid: Scattered declarations
function processUser(user) {
  console.log(name); // undefined (with var) or error (with let)
  // ... code ...
  var name = user.name;
  // ... more code ...
}

2. Use const by Default, let When Needed

const PI = 3.14159; // Never changes
const user = { name: 'A' }; // Reference doesn't change

let count = 0; // Will be reassigned
count++;

3. Avoid var in Modern JavaScript

// Modern JavaScript
const items = [];
let total = 0;

// Avoid
var items = [];
var total = 0;

4. Declare Functions Before Use (for readability)

// Clear and readable
function main() {
  doStep1();
  doStep2();
  doStep3();
}

function doStep1() {
  /* ... */
}
function doStep2() {
  /* ... */
}
function doStep3() {
  /* ... */
}

main();

Summary Table

DeclarationHoisted?Initial ValueAccessible Before Declaration?
function declarationYes (fully)The functionYes
varYesundefinedYes (returns undefined)
letYes (TDZ)UninitializedNo (ReferenceError)
constYes (TDZ)UninitializedNo (ReferenceError)
classYes (TDZ)UninitializedNo (ReferenceError)
Function expressionNo*N/ANo
Arrow functionNo*N/ANo

*The variable holding the expression is hoisted, but not the function itself.


Common Interview Questions

  1. What is hoisting?

    • Moving declarations to the top during the creation phase
  2. What's the difference between var and let hoisting?

    • Both are hoisted, but let is in TDZ until declaration
  3. Why does this log undefined?

    console.log(x);
    var x = 5;
    
    • var x is hoisted, but assignment isn't
  4. What's the Temporal Dead Zone?

    • The period where let/const exist but can't be accessed

Next Steps

Next, we'll explore the Event Loopβ€”how JavaScript handles asynchronous operations despite being single-threaded.

Skill Check

Test this lesson

Answer 4 quick questions to lock in the lesson and feed your adaptive practice queue.

--
Score
0/4
Answered
Not attempted
Status
1

Which module does this lesson belong to?

2

Which section is covered in this lesson content?

3

Which term is most central to this lesson?

4

What is the best way to use this lesson for real learning?

Your answers save locally first, then sync when account storage is available.
Practice queue