HuntExams Academy logo
JavaScriptBeginner#scope

What is hoisting?

Answer

During compilation declarations are registered before code runs. Function declarations are fully hoisted, var is hoisted and initialised to undefined, while let/const are hoisted but unusable until their declaration line.

Example

greet();            // works
function greet(){ console.log('hi'); }
console.log(x);     // undefined
var x = 5;
console.log(y);     // ReferenceError
let y = 5;

Detailed explanation

Hoisting describes JavaScript’s behaviour of setting up bindings for declarations before executing the statements in their scope. It does not mean the engine physically moves source code to the top. Instead, declaration-processing steps establish bindings in the relevant environment before normal execution begins. This matters because accessing a name before its declaration can return a value, return undefined, or throw an error, depending on the declaration type. In interviews, explain these differences rather than saying that all declarations move upward.

Ordinary function declarations are available with their function values before execution reaches their position in the surrounding scope. In the example, greet() works because the binding for greet already refers to the declared function. A var declaration behaves differently: its binding is created and initialised to undefined before the statements run, but its assignment happens only when execution reaches that statement. Therefore, console.log(x) prints undefined before var x = 5. The value 5 is not hoisted. Also, var belongs to the enclosing function scope, or the relevant top-level scope, rather than an ordinary block such as an if body.

Declarations using let and const also have bindings established ahead of execution, but those bindings remain uninitialised until their declarations are evaluated. The interval from entering their scope to initialisation is called the temporal dead zone, or TDZ. Accessing a binding during this interval throws a ReferenceError, so console.log(y) fails before let y = 5. Saying these declarations are hoisted but uninitialised is more precise than saying they are not hoisted. Both are block-scoped, and an inner binding can shadow an outer variable even while the inner binding is in its TDZ.

Function expressions and arrow functions follow the rules of the variable holding them, not the rules for function declarations. Calling a function expression assigned to var before its assignment usually produces a TypeError because the binding contains undefined; using let or const before initialisation produces a ReferenceError. Interviewers commonly test whether you can predict these outcomes, identify the relevant scope, and distinguish declaration setup from runtime assignment.

Key points to remember

  • Hoisting sets up bindings before statements execute; it does not physically relocate code.
  • Ordinary function declarations can be called before their textual declaration within their scope.
  • var bindings start as undefined; assignments take effect only when executed.
  • let and const remain uninitialised in the temporal dead zone, where access throws ReferenceError.
  • Function expressions and arrow functions depend on the declaration and assignment of their containing variable.

Common mistakes candidates make

  • Claiming that initialisers such as the 5 in var x = 5 are hoisted.
  • Saying let and const are simply not hoisted, without explaining their uninitialised bindings and TDZ.
  • Treating a function expression assigned to a variable like a function declaration.

Likely follow-up questions

What happens if you call an arrow function before assigning it?

With var, the binding initially contains undefined, so calling it before assignment throws a TypeError. With let or const, accessing it before initialisation throws a ReferenceError.

Does typeof avoid errors in the temporal dead zone?

No: typeof y throws a ReferenceError if y is a let or const binding currently in its TDZ. By contrast, typeof on a genuinely undeclared identifier returns the string "undefined".

Does execution continue after console.log(y) in the example?

Not normally: the uncaught ReferenceError stops execution of the remaining statements in that execution path. Consequently, let y = 5 is not reached in the example.

Interview tip: Define hoisting as binding setup, then walk through the example’s successful call, undefined value, and ReferenceError.

Related Interview Questions

1
JavaScriptIntermediate#closures#functions

What is a closure?

Open
2
JavaScriptBeginner#operators#coercion

Explain == vs ===.

Open
3
JavaScriptBeginner#coercion

What are truthy and falsy values?

Open