Variables & Hoisting
var ek purana dost hai jo kabhi nahi jaata — aur yahi problem hai.
JavaScript has three ways to declare variables, and understanding the difference between them is non-negotiable:
var name = "Sai"; // function scope, hoisted, redeclarable
let city = "Hyd"; // block scope, TDZ, not redeclarable
const PI = 3.14; // block scope, TDZ, not reassignable
var name = "Rahul"; // ✅ OK — var allows redeclaration
city = "Mumbai"; // ✅ OK — let allows reassignment
// PI = 3.15; // ❌ TypeError: Assignment to constant variable
Quick mental model:
var → "I do whatever I want, wherever I want"
let → "I stay inside my { } but you can change my value"
const → "I stay inside my { } AND you cannot change my value"
const obj = {a:1}; obj.a = 2; works fine. Only obj = {} would throw. For true immutability, use Object.freeze()."Scope" means where a variable is visible and accessible. JS has three scope levels:
// var leaks out of blocks
if (true) {
var leaked = "I escaped!";
let safe = "I stay here";
}
console.log(leaked); // "I escaped!" ← surprise!
console.log(safe); // ReferenceError ← expected
// var respects function boundary
function myFn() {
var localVar = "inside";
}
console.log(localVar); // ReferenceError — can't escape function
Three scope levels:
// 1. Global scope — declared outside any function/block
var globalVar = "everywhere";
// 2. Function scope — declared inside a function
function fn() { var funcVar = "inside fn only"; }
// 3. Block scope — inside { } (if, for, while, etc.)
if (true) { let blockVar = "inside block only"; }
let or const. var can leak across @if blocks in templates and cause subtle bugs that only appear in SSR. The Angular style guide explicitly forbids var.When JS runs your code, it does two passes:
// Pass 1 (Compilation): Collects all var and function declarations
// Pass 2 (Execution): Runs the code top to bottom
console.log(x); // undefined (not ReferenceError!) — var hoisted
var x = 5;
console.log(x); // 5
// Equivalent to what JS actually runs:
var x; // ← hoisted here by JS engine
console.log(x); // undefined
x = 5;
console.log(x); // 5
Function declarations are fully hoisted — the entire function body moves up:
greet(); // ✅ Works! — function declaration fully hoisted
function greet() { console.log("Hyderabad!"); }
sayHi(); // ❌ TypeError: sayHi is not a function
var sayHi = function() { console.log("Hi!"); }; // only var hoisted, not fn
function foo() {}), not a function expression (var foo = function() {}). The expression only hoists the var, leaving undefined.Temporal Dead Zone (TDZ) is the period between entering a block and reaching the let/const declaration line:
console.log(a); // undefined — var has no TDZ
var a = 1;
console.log(b); // ❌ ReferenceError: Cannot access 'b' before initialization
let b = 2;
// TDZ exists from { to the let line:
{
// ← TDZ for c starts here
console.log(c); // ❌ ReferenceError
let c = 3; // ← TDZ ends here
}
Even typeof throws in TDZ:
console.log(typeof undeclaredVar); // "undefined" — safe for undeclared
console.log(typeof myLet); // ❌ ReferenceError! (not "undefined")
let myLet = 5;
undefined (like var does). This catches real bugs early. Every let/const is hoisted — just not initialized until its line.The decision tree is simple:
// ✅ Default choice: const
const API_URL = "https://api.example.com";
const config = { theme: "dark" };
// ✅ When reassignment is needed: let
let retryCount = 0;
retryCount++;
for (let i = 0; i < items.length; i++) {
// let creates a new binding per iteration — closures work correctly
}
// ✅ Angular-specific patterns
const userService = inject(UserService); // never reassigned
const user = computed(() => userService.currentUser());
let isLoading = signal(false); // signal itself is const, .set() mutates internal
// ❌ Never use var
var oldStyle = "please no";
let in for loops matters: With var, all loop iterations share one variable. With let, each iteration gets its own copy. This is critical for closures inside loops — a classic interview question covered in Chapter 1.3 (Functions & Closures).Lo kar liya — Key Points:
- ✅
constby default,letwhen reassignment needed, nevervar - ✅
varis function-scoped;let/constare block-scoped - ✅
varis hoisted AND initialized (undefined);let/constare hoisted but NOT initialized (TDZ) - ✅ TDZ throws ReferenceError —
vargives undefined — know the difference for interviews - ✅
constdoesn't mean immutable — object properties can still change - ✅ Function declarations are fully hoisted; function expressions are NOT
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login