JavaScript Notes
Master JavaScript hoisting with deep explanations of variable hoisting, function hoisting, temporal dead zone, execution context phases, and interview questions with outputs.
What is Hoisting?
Hoisting is JavaScript's default behavior of moving declarations to the top of their scope during the compilation phase. Before any code executes, the JavaScript engine scans the entire code and allocates memory for variable and function declarations.
Important: Only declarations are hoisted, NOT initializations or assignments.
How the JavaScript Engine Works
JavaScript execution happens in two phases:
- Creation Phase (Memory Allocation) - Engine scans code and allocates memory
- Execution Phase - Engine executes code line by line
Source Code:
console.log(x);
var x = 10;
Creation Phase:
+---------------------------+
| Variable: x = undefined |
| (memory allocated) |
+---------------------------+
Execution Phase:
Line 1: console.log(x) -> undefined
Line 2: x = 10 (assignment happens now)Variable Hoisting
var Hoisting
var declarations are hoisted and initialized with undefined.
console.log(name); // undefined (not ReferenceError)
console.log(age); // undefined
var name = "JavaScript";
var age = 28;
console.log(name); // "JavaScript"
console.log(age); // 28undefined undefined JavaScript 28
How the Engine Sees It
// What you write:
console.log(x);
var x = 5;
// How engine interprets it:
var x; // Declaration hoisted
console.log(x); // undefined
x = 5; // Assignment stays in placelet and const Hoisting
let and const ARE hoisted but NOT initialized. They remain in the Temporal Dead Zone (TDZ) until the declaration is reached.
// console.log(a); // ReferenceError: Cannot access 'a' before initialization
// console.log(b); // ReferenceError: Cannot access 'b' before initialization
let a = 10;
const b = 20;
console.log(a); // 10
console.log(b); // 20TDZ Memory Diagram
Block Start:
+-----------------------------------+
| let x: <uninitialized> (TDZ) | <- ReferenceError if accessed
| const y: <uninitialized> (TDZ) | <- ReferenceError if accessed
+-----------------------------------+
After Declaration:
+-----------------------------------+
| let x: 10 (initialized) | <- Safe to access
| const y: 20 (initialized) | <- Safe to access
+-----------------------------------+Function Hoisting
Function Declarations - Fully Hoisted
Function declarations are hoisted completely — both the name and the function body.
// Can call before declaration
greet("Alice");
multiply(3, 4);
function greet(name) {
console.log(`Hello, ${name}!`);
}
function multiply(a, b) {
console.log(a * b);
}Hello, Alice! 12
Function Expressions - NOT Fully Hoisted
// TypeError: sayHi is not a function
// sayHi();
var sayHi = function() {
console.log("Hi!");
};
sayHi(); // "Hi!" - works after assignmentHi!
How Engine Sees Function Expressions
// What you write:
sayHello();
var sayHello = function() { console.log("Hello"); };
// How engine interprets:
var sayHello; // Hoisted as undefined
sayHello(); // TypeError: sayHello is not a function
sayHello = function() { console.log("Hello"); };Arrow Functions - Same as Function Expressions
Hoisting Priority
When both a variable and function have the same name, function declarations take priority.
console.log(typeof foo); // "function"
var foo = "string";
function foo() {
return "function";
}
console.log(typeof foo); // "string"function string
Explanation
Creation Phase:
1. var foo -> allocated (undefined)
2. function foo -> allocated (overwrites var, stores function body)
Execution Phase:
1. typeof foo -> "function" (function declaration won)
2. foo = "string" -> reassigns foo to string
3. typeof foo -> "string"Hoisting in Different Contexts
Inside Functions
function demo() {
console.log(x); // undefined
// console.log(y); // ReferenceError (TDZ)
var x = 10;
let y = 20;
console.log(x); // 10
}
demo();Inside Loops
undefined Loop: 0 Loop: 1 Loop: 2 After: 3
Inside Blocks
{
// var ignores block scope
var blockVar = "I escape";
}
console.log(blockVar); // "I escape"
{
// let/const respect block scope
let blockLet = "I stay";
}
// console.log(blockLet); // ReferenceErrorClass Hoisting
Classes are hoisted but remain in TDZ (similar to let).
// ReferenceError: Cannot access 'Animal' before initialization
// const cat = new Animal("Cat");
class Animal {
constructor(name) {
this.name = name;
}
}
const cat = new Animal("Cat");
console.log(cat.name); // "Cat"Class Expression
// TypeError: Dog is not a constructor
// const d = new Dog();
var Dog = class {
constructor(name) {
this.name = name;
}
};
const d = new Dog("Rex");
console.log(d.name); // "Rex"Import Hoisting
Import statements are hoisted to the top of the module.
// This works because imports are hoisted
console.log(add(2, 3)); // 5
import { add } from './math.js';Tricky Hoisting Examples
Example 1: Function in if block
console.log(foo); // undefined (var hoisted)
if (true) {
function foo() { return 1; }
}
console.log(foo); // function (assigned after block executes)Example 2: Multiple declarations
var a = 1;
function a() { return 2; }
console.log(a); // 1
// Engine sees:
// function a() { return 2; } <- function hoisted first
// var a; <- ignored (already declared)
// a = 1; <- assignment
// console.log(a); <- 1Example 3: Nested functions
function outer() {
console.log(inner); // function (fully hoisted)
function inner() {
return "I am inner";
}
console.log(inner()); // "I am inner"
}
outer();[Function: inner] I am inner
Example 4: var in switch
Hoisting and Closures Together
3 3 3
Common Mistakes
Mistake 1: Assuming let/const behave like var
function mistake1() {
// console.log(x); // ReferenceError (TDZ)
let x = 5;
console.log(x); // 5
}Mistake 2: Relying on function expression hoisting
// greet(); // TypeError: greet is not a function
var greet = function() { console.log("Hi"); };Mistake 3: Conditional function declarations
// Behavior varies across engines!
if (true) {
function foo() { return 1; }
} else {
function foo() { return 2; }
}
// Don't rely on this patternCommon Traps 🪤
| Code | Behavior | Reason |
|---|---|---|
console.log(x); var x = 1 | undefined | var hoisted + initialized to undefined |
console.log(y); let y = 1 | ReferenceError | TDZ — not initialized |
fn(); function fn(){} | Works | Function declaration fully hoisted |
fn(); var fn = ()=>{} | TypeError | fn is undefined, not a function |
var x; function x(){} | typeof x === "function" | Function hoisting wins |
class Foo{} new Foo() | Works | class TDZ ends at declaration |
Interview Questions
Q1: What is the output?
var x = 10;
function foo() {
console.log(x);
var x = 20;
console.log(x);
}
foo();Answer: undefined then 20. Local var x shadows global and is hoisted as undefined.
Q2: What is the output?
console.log(foo());
function foo() { return 1; }
var foo = function() { return 2; };
console.log(foo());Answer: 1 then 2. Function declaration hoisted first, then var assignment overwrites.
Q3: What is the output?
let x = 1;
{
// console.log(x); // What happens?
let x = 2;
}Answer: ReferenceError. The inner let x creates a TDZ from block start to declaration.
Q4: Explain the difference
foo(); // Works
bar(); // TypeError
function foo() { console.log("foo"); }
var bar = function() { console.log("bar"); };Answer: Function declarations are fully hoisted (name + body). Function expressions only hoist the variable name (as undefined).
Q5: What is the output?
var a = 1;
function outer() {
console.log(a);
var a = 2;
function inner() {
console.log(a);
var a = 3;
}
inner();
console.log(a);
}
outer();
console.log(a);Answer: undefined, undefined, 2, 1. Each function scope has its own hoisted var a.
Hoisting Rules Summary
| Declaration Type | Hoisted? | Initialized? | Scope |
|---|---|---|---|
| var | Yes | undefined | Function |
| let | Yes | No (TDZ) | Block |
| const | Yes | No (TDZ) | Block |
| function declaration | Yes | Full body | Function |
| function expression (var) | var only | undefined | Function |
| function expression (let/const) | Yes | No (TDZ) | Block |
| class | Yes | No (TDZ) | Block |
| import | Yes | Fully | Module |
Best Practices
- Declare variables at the top of their scope
- Use const/let instead of var to avoid hoisting confusion
- Define functions before using them for readability
- Avoid relying on hoisting — write code as if hoisting doesn't exist
- Use strict mode to catch undeclared variables
- Don't declare functions inside blocks conditionally
Summary
Hoisting moves declarations to the top of their scope during compilation. var is hoisted with undefined, let/const are hoisted into TDZ, function declarations are fully hoisted. Understanding hoisting prevents bugs and is crucial for JavaScript interviews.
Practice Exercises
- Predict the output of 10 hoisting examples
- Convert code that relies on hoisting to explicit ordering
- Identify hoisting-related bugs and fix them
- Explain why function declarations are preferred in certain cases
- Create examples demonstrating TDZ behavior
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Hoisting in JavaScript — Complete Guide with Examples 2026.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this JavaScript Master Course topic.
Search Terms
javascript-complete-guide, javascript master course, javascript, complete, guide, advanced, hoisting, hoisting in javascript — complete guide with examples 2026
Related JavaScript Master Course Topics