What is hoisting in JavaScript?

Beginner8 min interview
Skills tested:
Compilation phase vs execution phase distinctionvar hoisting and undefined initializationTemporal dead zone for let and constFunction declaration vs function expression hoistingScope-aware reasoning about declaration order

Advertisement

🧩 Scenario

In a real codebase, you will encounter hoisting when reading legacy code that uses var declarations, when debugging ReferenceError throws that happen before an assignment, and when reasoning about why a function can be called before it is defined in the file. Understanding the temporal dead zone is particularly important in modern code because using let or const before their declaration produces a ReferenceError instead of silently returning undefined like var does.

Architecture Walkthrough

What Happens During the Compilation Phase

Before JavaScript executes a script, the engine scans the code in a compilation phase. During this phase, all variable declarations (var, let, const) and function declarations are registered in the appropriate scope. This registration is what hoisting refers to: the declarations are "moved to the top" conceptually before any code runs.

For var, the declaration is registered and immediately initialized to undefined. This is why accessing a var variable before its assignment in the source code returns undefined rather than throwing. The declaration exists; the value just has not been assigned yet.

The Temporal Dead Zone for let and const

let and const are also hoisted, but unlike var, they are not initialized. The period between when the binding is created (at the start of its scope) and when the declaration is reached in the code is called the Temporal Dead Zone (TDZ). Any access to the variable during the TDZ throws a ReferenceError.

This behavior is intentional. The TDZ prevents the silent undefined bugs that var hoisting enables. If you read a let variable before declaring it, you get a clear, loud error rather than a confusing undefined that hides the real problem.

Function Declarations vs Function Expressions

Function declarations are hoisted completely, meaning both the declaration and the function body are available before the code that defines them runs. You can call a function declaration on line 1 even if it appears on line 100.

Function expressions (including arrow functions assigned to variables) are not fully hoisted. If assigned to a var, only the variable declaration is hoisted and initialized to undefined. Calling it before the assignment throws a TypeError because you are trying to invoke undefined. If assigned to a let or const, the TDZ applies and access before the declaration throws a ReferenceError.


Key Code Explained

// var: declaration hoisted, initialized to undefined
console.log(x); // undefined (no error)
var x = 10;
console.log(x); // 10

// let: hoisted but not initialized — temporal dead zone
console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 20;

// Function declaration: fully hoisted
greet(); // 'Hello' — works before the definition
function greet() {
  console.log('Hello');
}

// Function expression: only var declaration is hoisted
sayBye(); // TypeError: sayBye is not a function
var sayBye = function () {
  console.log('Bye');
};

The sayBye example is the most commonly misunderstood. Developers expect either a ReferenceError (because it feels like it is not defined) or for it to work (because they know function expressions can be called). The actual result is a TypeError because sayBye exists as undefined at that point, and calling undefined as a function produces a type error.


Tradeoffs

DeclarationHoistedInitializedAccess before declaration
varYesYes (undefined)Returns undefined silently
letYesNoReferenceError (TDZ)
constYesNoReferenceError (TDZ)
function declarationYesYes (full body)Works correctly
function expression (var)PartiallyundefinedTypeError on call

What Interviewers Actually Check

  • Whether you know that var returns undefined before assignment rather than throwing
  • Whether you can explain the temporal dead zone without confusing it with "not hoisted"
  • Whether you understand that let and const are hoisted but in an uninitialized state
  • Whether you can distinguish function declaration hoisting from function expression hoisting
  • Whether you know that accessing a variable in the TDZ produces a ReferenceError, not undefined

Follow-Up Questions

  1. Are class declarations subject to the temporal dead zone? What happens if you instantiate a class before its declaration?
  2. How does hoisting behave differently inside a function scope versus the global scope?
  3. If JavaScript did not have hoisting at all, what coding pattern would break that is currently allowed?
  4. Linters like ESLint have a no-use-before-define rule. What problem does it prevent that hoisting enables?
  5. A teammate says they always put all var declarations at the top of every function to make hoisting explicit. Is this a good practice? What would you recommend instead?

Common Candidate Mistakes

  • Saying let and const are not hoisted at all, when they are hoisted but left uninitialized
  • Expecting a var variable accessed before its declaration to throw when it silently returns undefined
  • Assuming a function expression assigned to var is fully hoisted when only the variable declaration is registered
  • Not knowing that class declarations are also subject to the temporal dead zone
  • Confusing hoisting with execution order: hoisting only affects the declaration, not the assignment or the value

Interview Readiness Checklist

Before you leave this question, make sure you can answer:

  • Can you explain what the JavaScript engine does during the compilation phase before executing code?
  • Can you predict what accessing a var variable before its declaration returns?
  • Can you explain the temporal dead zone and what error it produces?
  • Can you distinguish hoisting behavior between function declarations and function expressions?
  • Can you state why let and const were introduced to address var hoisting problems?

Summary

Hoisting is the result of JavaScript's two-phase execution model. During compilation, declarations are registered in their scope before any code runs. For var, this means the variable exists and holds undefined from the start of its scope. For let and const, the binding exists but is uninitialized, creating the Temporal Dead Zone where any access throws a ReferenceError.

Function declarations are hoisted with their full body, making them callable anywhere in their scope regardless of where they appear in the source. Function expressions behave like their variable assignment: only the variable is hoisted, not the function, so calling them before the assignment throws a TypeError.

Understanding hoisting explains a category of bugs that appear in legacy JavaScript code using var and helps you understand why let and const were designed to fail loudly rather than silently when accessed before declaration.

Related Videos
Watch these videos to learn more about this topic
Frequently Asked Questions

Does let or const get hoisted?

Yes, but they remain uninitialized until declared — this is called the temporal dead zone.

Advertisement


Stay Updated

Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.

Advertisement