Intermediate1 questionFull Guide

JavaScript Hoisting Interview Questions

Hoisting is a top-10 JavaScript interview topic. Learn how var, let, const, and function declarations are handled at parse time.

The Mental Model

Picture a stage director before a play starts. Before the curtain rises, they read the entire script and make a list of every actor and every prop. When the play actually begins, the director already knows every name — even before that actor walks on stage. JavaScript does the same thing before running your code. It scans the entire file, finds every variable declaration and every function declaration, and registers them in memory first. Then — and only then — it starts executing line by line. That pre-scan is hoisting. The key insight: declarations move to the top of their scope before execution begins. But for variables declared with var, only the declaration moves — not the value. The value stays where you wrote it.

The Explanation

What hoisting actually is

Hoisting is not a physical movement of code. JavaScript doesn't rewrite your file. What actually happens is that the JavaScript engine runs in two phases:

  1. Creation phase — the engine scans your code and allocates memory for all declarations before a single line executes.
  2. Execution phase — your code runs line by line.

The result behaves as if declarations were moved to the top. That's hoisting.

var — hoisted and initialized to undefined

console.log(name)  // undefined — not a ReferenceError
var name = 'Alice'
console.log(name)  // 'Alice'

During the creation phase, JavaScript registers name and sets it to undefined. During execution, the first console.log runs before the assignment — so it gets undefined. The assignment name = 'Alice' then runs, and the second log gets 'Alice'.

What the engine effectively sees:

var name = undefined  // hoisted — declaration + initialization

console.log(name)     // undefined
name = 'Alice'        // assignment stays here
console.log(name)     // 'Alice'

let and const — hoisted but NOT initialized (the Temporal Dead Zone)

console.log(age)  // ReferenceError: Cannot access 'age' before initialization
let age = 25

let and const ARE hoisted — the engine knows they exist. But unlike var, they are not initialized to undefined. They sit in the Temporal Dead Zone (TDZ) from the start of their scope until the declaration is reached. Accessing them during the TDZ throws a ReferenceError.

// TDZ starts here for 'score'
console.log(score)  // ReferenceError — in TDZ

const score = 100   // TDZ ends here
console.log(score)  // 100

The TDZ exists to catch bugs. var's silent undefined hid real errors for years.

Function declarations — fully hoisted

greet('Alice')  // 'Hello, Alice' — works perfectly before the declaration

function greet(name) {
  console.log(`Hello, ${name}`)
}

Function declarations are hoisted completely — both the declaration AND the body. You can call them before they appear in the source. This is intentional: it lets you put helper functions at the bottom of a file and the main logic at the top.

Function expressions — NOT fully hoisted

greet('Alice')  // TypeError: greet is not a function

var greet = function(name) {
  console.log(`Hello, ${name}`)
}

Here greet is a var — so it's hoisted and initialized to undefined. Calling undefined as a function gives a TypeError. The function body is not hoisted — it stays where you wrote it.

// What the engine sees:
var greet = undefined

greet('Alice')  // TypeError: undefined is not a function

greet = function(name) {
  console.log(`Hello, ${name}`)
}

Arrow functions — same as function expressions

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

const sayHi = () => console.log('Hi')

Arrow functions are always assigned to variables. const puts it in the TDZ — accessing before declaration is a ReferenceError. var would give TypeError (calling undefined). Either way, it doesn't work before the declaration line.

Hoisting in the same scope — order matters

var x = 1

function example() {
  console.log(x)  // undefined — NOT 1
  var x = 2
  console.log(x)  // 2
}

example()

The var x inside example is hoisted to the top of that function's scope. So inside the function, x is a completely separate variable from the global x. The first log gets the local undefined, not the global 1. This surprises most developers.

Class declarations — hoisted but in TDZ

const p = new Person('Alice')  // ReferenceError — in TDZ
class Person {
  constructor(name) { this.name = name }
}

Like let and const, class declarations are hoisted but stay in the TDZ. You cannot use a class before its declaration. This is correct behaviour — classes should be declared before use.

The complete hoisting cheatsheet

DeclarationHoisted?Initialized toBefore declaration
var x✓ Yesundefinedundefined
let x✓ YesTDZ (uninitialized)ReferenceError
const x✓ YesTDZ (uninitialized)ReferenceError
function f(){}✓ YesFull function bodyWorks ✓
var f = function(){}✓ Yes (var part)undefinedTypeError
const f = () => {}✓ Yes (const part)TDZReferenceError
class C {}✓ YesTDZReferenceError

Interview trap — multiple declarations in same scope

function test() {
  console.log(typeof foo)  // 'function' — not 'undefined'

  var foo = 'bar'

  function foo() {
    return 'I am a function'
  }

  console.log(typeof foo)  // 'string'
}

test()

When a function declaration and a var declaration share the same name, the function declaration wins during hoisting. The engine hoists the function fully, then the var declaration is ignored (since the name already exists). The assignment foo = 'bar' still runs during execution, overwriting the function.

Common Misconceptions

⚠️

Many devs think hoisting physically moves code to the top of the file — but actually JavaScript never rewrites your code. Hoisting is a mental model for the two-phase execution: declarations are registered in memory before the file runs, not physically relocated.

⚠️

Many devs think let and const are not hoisted — but actually they are hoisted, just not initialized. They sit in the Temporal Dead Zone from the start of their scope until their declaration line. The ReferenceError you get is proof the engine knows they exist.

⚠️

Many devs think undefined means the variable doesn't exist — but actually with var, undefined means the variable was declared but hasn't been assigned yet. A true undeclared variable throws ReferenceError, not undefined.

⚠️

Many devs think function expressions are fully hoisted like function declarations — but actually only the variable name is hoisted (to undefined). The function body stays where you wrote it. Calling it before the assignment gives TypeError: not a function.

⚠️

Many devs think hoisting only applies to the top of the file — but actually hoisting happens at the top of every scope: global scope, function scope, and block scope. A var inside a function hoists to the top of that function, not the file.

⚠️

Many devs think you can use a class before it's declared because "everything is hoisted" — but actually classes are in the Temporal Dead Zone just like let and const. Using a class before its declaration throws ReferenceError.

Where You'll See This in Real Code

Linting rules like no-use-before-define exist entirely because of hoisting confusion — they force you to declare before use, preventing the silent undefined bugs that var hoisting causes.

Function declaration hoisting is why many codebases put exported functions at the top of a file and helper/utility functions at the bottom — the helpers are hoisted and available even though they appear later in the source.

The Temporal Dead Zone for let and const was specifically designed to catch real bugs — before ES6, var's silent undefined was masking errors in production code where variables were accidentally used before assignment.

React's useEffect dependency array issues are often rooted in hoisting confusion — developers reference variables before they're fully initialized in the render cycle, causing stale closure bugs that look like hoisting problems.

Build tools like Webpack and esbuild handle module hoisting — they analyze import/export declarations first (hoisting them conceptually) before executing module code, which is why you can't conditionally import a module in ES modules.

TypeScript enforces strict declaration order and reports errors when you access variables before their let/const declarations, essentially enforcing what the TDZ does at runtime but at compile time.

Interview Cheat Sheet

  • var is hoisted + initialized to undefined — usable before declaration but gives undefined
  • let/const are hoisted but in TDZ — accessing before declaration throws ReferenceError
  • function declarations are fully hoisted — callable before they appear in source
  • function expressions (var/let/const = function) — only the binding is hoisted, not the body
  • Hoisting is per-scope, not just global — a var inside a function hoists to that function's top
  • When var and function share a name — function declaration wins during hoisting phase
💡

How to Answer in an Interview

  • 1.Always distinguish "hoisted" (all declarations) from "initialized" (only var and function decls)
  • 2.TDZ is the follow-up question to every hoisting question — mention it proactively
  • 3.Prove understanding by predicting output of code with mixed var/let declarations
  • 4.Explain WHY: hoisting happens during the Creation Phase of the Execution Context
  • 5.Connect to real bugs: "This is why you'd see undefined instead of a ReferenceError in legacy code"
  • 6.Draw the two-phase model (creation → execution) when explaining — it's clearer than saying "moved to top"
📖 Deep Dive Articles
var vs let vs const in JavaScript: The Complete Comparison9 min readTop 50 JavaScript Interview Questions (With Deep Answers)18 min readJavaScript Scope Explained: Lexical Scope, Scope Chain, and How Variables Are Found10 min readJavaScript Hoisting Explained: What Actually Moves, What Doesn't, and Why It Matters9 min readJavaScript Closures: What They Actually Are (And Why Interviews Love Them)10 min readJavaScript Output Questions: 30 Tricky Examples That Test Real Understanding14 min read

Practice Questions

1 question
#01

What is hoisting in JavaScript?

🟢 EasyCore JS💡 Declarations are moved to top of scope before execution

Related Topics

JavaScript var, let, and const Interview Questions
Beginner·3–5 Qs
JavaScript Scope Interview Questions
Beginner·4–6 Qs
JavaScript Execution Context Interview Questions
Advanced·3–6 Qs
🎯

Can you answer these under pressure?

Reading answers is not the same as knowing them. Practice saying them out loud with AI feedback — that's what builds real interview confidence.

Practice Free →Try Output Quiz