Control Flow
Conditionals, loops, and every way to control the execution path of your JavaScript code
Last updated on
Control flow determines which code runs and how many times it runs. Mastering these patterns makes you write efficient, readable logic.
Conditionals — Making Decisions
if / else if / else
const score = 85;
if (score >= 90) {
console.log("Grade: A");
} else if (score >= 80) {
console.log("Grade: B"); // ← this runs
} else if (score >= 70) {
console.log("Grade: C");
} else {
console.log("Grade: F");
}switch
Best when comparing one value against multiple options:
const role = "admin";
switch (role) {
case "admin":
console.log("Full access");
break; // ← forgetting break causes fall-through!
case "editor":
console.log("Edit access");
break;
case "viewer":
console.log("Read only");
break;
default:
console.log("Unknown role");
}Common mistake — forgetting break:
switch (day) {
case "Mon":
case "Tue":
case "Wed":
case "Thu":
case "Fri":
console.log("Weekday"); // intentional fall-through
break;
case "Sat":
case "Sun":
console.log("Weekend");
break;
}Ternary Operator
For simple if/else in one line:
const status = age >= 18 ? "Adult" : "Minor";
// Don't nest ternaries — use if/else instead
// ❌ Hard to read
const grade = score >= 90 ? "A" : score >= 80 ? "B" : "C";
// ✅ Clear
let grade;
if (score >= 90) grade = "A";
else if (score >= 80) grade = "B";
else grade = "C";Loops — Repeating Actions
for Loop
The classic — use when you know how many iterations:
for (let i = 0; i < 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}
// Counting backwards
for (let i = 10; i > 0; i--) {
console.log(i); // 10, 9, 8, ... 1
}while Loop
Use when you don't know how many iterations:
let attempts = 0;
while (attempts < 3) {
const success = tryConnect();
if (success) break;
attempts++;
}do...while Loop
Guarantees at least one execution:
let input;
do {
input = prompt("Enter a number greater than 10:");
} while (Number(input) <= 10);for...of — Iterate Over Values (ES6)
Works with arrays, strings, Maps, Sets — any iterable:
const colors = ["red", "green", "blue"];
for (const color of colors) {
console.log(color); // "red", "green", "blue"
}
// With strings
for (const char of "hello") {
console.log(char); // "h", "e", "l", "l", "o"
}
// With index (using entries)
for (const [index, color] of colors.entries()) {
console.log(`${index}: ${color}`);
}for...in — Iterate Over Keys
Works with objects (and arrays, but avoid it for arrays):
const user = { name: "Shiva", age: 25, city: "Mumbai" };
for (const key in user) {
console.log(`${key}: ${user[key]}`);
}
// name: Shiva
// age: 25
// city: Mumbaifor...of vs for...in — Critical Difference
for...of | for...in | |
|---|---|---|
| Iterates | Values | Keys (property names) |
| Best for | Arrays, Strings, Maps, Sets | Objects |
| On arrays | [1, 2, 3] → 1, 2, 3 | [1, 2, 3] → "0", "1", "2" |
| Includes prototype? | No | Yes (use hasOwnProperty to filter) |
// ❌ DON'T use for...in with arrays
const arr = [10, 20, 30];
for (const key in arr) {
console.log(key); // "0", "1", "2" (strings, not numbers!)
console.log(typeof key); // "string"
}
// ✅ DO use for...of with arrays
for (const value of arr) {
console.log(value); // 10, 20, 30
}Break and Continue
break — Exit the Loop Entirely
for (let i = 0; i < 10; i++) {
if (i === 5) break;
console.log(i); // 0, 1, 2, 3, 4
}continue — Skip Current Iteration
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) continue; // skip even numbers
console.log(i); // 1, 3, 5, 7, 9
}Labeled Statements — Breaking Nested Loops
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) break outer; // breaks BOTH loops
console.log(i, j);
}
}
// 0 0, 0 1, 0 2, 1 0Real-World Patterns
Early Return Pattern (Avoids Nesting)
// ❌ Deeply nested
function processUser(user) {
if (user) {
if (user.isActive) {
if (user.hasPermission) {
// finally do the work
return doWork(user);
}
}
}
return null;
}
// ✅ Guard clauses with early return
function processUser(user) {
if (!user) return null;
if (!user.isActive) return null;
if (!user.hasPermission) return null;
return doWork(user);
}Object Lookup Instead of Switch
// ❌ Verbose switch
function getStatusText(code) {
switch (code) {
case 200: return "OK";
case 404: return "Not Found";
case 500: return "Server Error";
default: return "Unknown";
}
}
// ✅ Clean object lookup
const STATUS_TEXT = {
200: "OK",
404: "Not Found",
500: "Server Error"
};
function getStatusText(code) {
return STATUS_TEXT[code] ?? "Unknown";
}Processing Array Until Condition
const items = [1, 2, 3, -1, 4, 5];
// Process until we hit a negative number
const result = [];
for (const item of items) {
if (item < 0) break;
result.push(item * 2);
}
// result: [2, 4, 6]Common Mistakes
1. Infinite Loop
// ❌ Forgot to update counter
let i = 0;
while (i < 5) {
console.log(i);
// missing i++ → runs forever
}2. Off-by-One Error
const arr = [1, 2, 3, 4, 5];
// ❌ Uses <= instead of <
for (let i = 0; i <= arr.length; i++) {
console.log(arr[i]); // last iteration gives undefined
}
// ✅ Correct
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}3. Modifying Array While Iterating
// ❌ Removing items during for loop — skips elements
const arr = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {
if (arr[i] % 2 === 0) arr.splice(i, 1);
}
// arr: [1, 3, 5] — works accidentally, but buggy approach
// ✅ Use filter instead
const odds = arr.filter(n => n % 2 !== 0);