DEV Community

Cover image for JavaScript Hoisting — Variables, Functions, and TDZ.
CodeScythe
CodeScythe

Posted on • Edited on

JavaScript Hoisting — Variables, Functions, and TDZ.

  • Hoisting is a JavaScript mechanism where the engine creates a variable environment during compilation, registering all variable and function declarations in memory.

  • For beginners, it’s often helpful to think of these declarations as being “moved to the top” of their containing scope, but technically the engine just sets up the environment before execution begins.

  • This allows you to reference variables and functions before they are declared — but the behavior depends on whether you’re using var, let, const, or function declarations.

How JavaScript actually runs code

When JS executes, it doesn’t just run line by line immediately. The JS engine (like V8 in Chrome or Node.js) does two phases for each scope:

1. Creation Phase

  • The engine scans the code.
  • Creates a variable environment for the scope.
  • Registers all variable and function declarations.
  • Sets up memory bindings:

    • varundefined
    • let / const → exist in Temporal Dead Zone (TDZ)
    • function declarations → full function object in memory

2. Execution Phase

  • Runs the code line by line.
  • Assigns values to variables.
  • Calls functions.

Hoisting in Action

Var example

console.log(a); // undefined
var a = 10;
Enter fullscreen mode Exit fullscreen mode
  • Creation phase: a exists in memory → undefined
  • Execution phase: a = 10 runs

Let / Const example

console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 20;

console.log(c); // ReferenceError: Cannot access 'c' before initialization
const c = 30;
Enter fullscreen mode Exit fullscreen mode
  • TDZ: period from the start of scope until initialization. Accessing let/const in TDZ throws a ReferenceError.

Function declarations

foo(); // Works

function foo() {
    console.log("Hoisted function!");
}
Enter fullscreen mode Exit fullscreen mode
  • Functions are hoisted with their entire body, so you can call them before they appear in code.

Quick Table: Hoisting by Declaration Type

Declaration Hoisted? Initialization Timing Accessible before initialization?
var Yes undefined at compile time Yes (undefined)
let Yes (binding exists) Only when execution reaches declaration No — TDZ
const Yes (binding exists) Must be initialized at declaration No — TDZ
function Yes (entire function hoisted) Immediately available Yes

What the Engine Actually Does

Global Execution Context Lifecycle

Global Execution Context
│
├─ Creation Phase
│    ├─ var → undefined
│    ├─ let/const → TDZ (cannot be accessed)
│    └─ function → full function object
│
└─ Execution Phase
     ├─ Assign values
     ├─ Execute code line by line
     └─ Create new Execution Contexts for functions
Enter fullscreen mode Exit fullscreen mode

Common Pitfalls

  1. Accessing let/const before declaration
console.log(x); // ReferenceError
let x = 10;
Enter fullscreen mode Exit fullscreen mode
  1. Calling functions assigned to var vs function declarations
console.log(fooVar); // undefined
var fooVar = function() { console.log("Hi"); };

fooFunc(); // Works
function fooFunc() { console.log("Hello"); }
Enter fullscreen mode Exit fullscreen mode
  1. Thinking object literals affect hoisting
console.log(obj.a); // ReferenceError if obj uses let/const inside TDZ
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  • Hoisting happens before code executes.
  • var → hoisted, initialized to undefined
  • let/const → hoisted, but in TDZ
  • Functions → fully hoisted
  • Understanding hoisting prevents undefined errors and ReferenceErrors.

Top comments (0)