Master control flow, loops, conditional statements, and error handling in JavaScript.
Master control flow, loops, conditional statements, and error handling in JavaScript.
Learn conditional statements and decision-making logic in JavaScript with modern patterns and best practices
Content by: Ronak Gajera
React.js Developer
Conditional statements allow your program to make decisions and execute different code blocks based on whether certain conditions are true or false. JavaScript provides several ways to implement conditional logic.
// Basic if statement
let age = 18;
if (age >= 18) {
console.log("You are an adult");
}
// If...else statement
if (age >= 18) {
console.log("You can vote");
} else {
console.log("You cannot vote yet");
}
// If...else if...else chain
let score = 85;
if (score >= 90) {
console.log("Grade: A");
} else if (score >= 80) {
console.log("Grade: B");
} else if (score >= 70) {
console.log("Grade: C");
} else if (score >= 60) {
console.log("Grade: D");
} else {
console.log("Grade: F");
}
// Nested if statements
let isLoggedIn = true;
let hasPermission = false;
if (isLoggedIn) {
if (hasPermission) {
console.log("Access granted");
} else {
console.log("Access denied - insufficient permissions");
}
} else {
console.log("Please log in first");
}
// Ternary operator (shorthand)
let status = age >= 18 ? "adult" : "minor";
let message = `You are an ${status}`;
// Multiple conditions with logical operators
let isStudent = true;
let hasDiscount = false;
let age2 = 25;
if (isStudent && age2 < 30) {
hasDiscount = true;
console.log("Student discount applied");
} else if (age2 >= 65) {
hasDiscount = true;
console.log("Senior discount applied");
} else {
console.log("No discount available");
}
// Modern conditional patterns (2025)
// Optional chaining with conditionals
let user = {
profile: {
settings: {
notifications: true
}
}
};
if (user?.profile?.settings?.notifications) {
console.log("Notifications are enabled");
} else {
console.log("Notifications are disabled or not configured");
}
// Nullish coalescing with conditionals
let theme = user?.profile?.settings?.theme ?? 'light';
console.log(`Current theme: ${theme}`);
// Short-circuit evaluation
let isAdmin = user?.role === 'admin';
let canEdit = isAdmin || user?.permissions?.includes('edit');
if (canEdit) {
console.log("User can edit content");
}
// Conditional object properties
let config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
...(isAdmin && { debugMode: true }),
...(user?.preferences?.theme && { theme: user.preferences.theme })
};
// Conditional function calls
let logger = {
info: (msg) => console.log(`[INFO] ${msg}`),
error: (msg) => console.error(`[ERROR] ${msg}`),
debug: (msg) => console.debug(`[DEBUG] ${msg}`)
};
let logLevel = 'info';
let message = 'User logged in';
if (logLevel === 'debug') {
logger.debug(message);
} else if (logLevel === 'error') {
logger.error(message);
} else {
logger.info(message);
}
// Switch-like pattern with objects
const actions = {
'create': () => console.log('Creating...'),
'read': () => console.log('Reading...'),
'update': () => console.log('Updating...'),
'delete': () => console.log('Deleting...')
};
let action = 'create';
if (actions[action]) {
actions[action]();
} else {
console.log('Invalid action');
}
// Exercise: Build a Grade Calculator
function calculateGrade(score) {
if (score >= 90) {
return 'A';
} else if (score >= 80) {
return 'B';
} else if (score >= 70) {
return 'C';
} else if (score >= 60) {
return 'D';
} else {
return 'F';
}
}
// Exercise: Build a User Access Control System
function checkAccess(user, resource) {
// Check if user exists and is active
if (!user || !user.isActive) {
return { allowed: false, reason: 'User not found or inactive' };
}
// Check if user has required permissions
if (!user.permissions || !user.permissions.includes(resource)) {
return { allowed: false, reason: 'Insufficient permissions' };
}
// Check if user is admin (bypass all restrictions)
if (user.role === 'admin') {
return { allowed: true, reason: 'Admin access' };
}
// Check time-based restrictions
const currentHour = new Date().getHours();
if (currentHour < 6 || currentHour > 22) {
return { allowed: false, reason: 'Access restricted outside business hours' };
}
return { allowed: true, reason: 'Access granted' };
}
// Test the functions
console.log(calculateGrade(95)); // 'A'
console.log(calculateGrade(85)); // 'B'
console.log(calculateGrade(75)); // 'C'
console.log(calculateGrade(55)); // 'F'
const user1 = {
id: 1,
name: 'John',
isActive: true,
role: 'user',
permissions: ['read', 'write']
};
const user2 = {
id: 2,
name: 'Admin',
isActive: true,
role: 'admin',
permissions: ['read', 'write', 'delete']
};
console.log(checkAccess(user1, 'read')); // { allowed: true, reason: 'Access granted' }
console.log(checkAccess(user2, 'delete')); // { allowed: true, reason: 'Admin access' }
console.log(checkAccess(null, 'read')); // { allowed: false, reason: 'User not found or inactive' }
Test your understanding of this topic:
Master switch statements for handling multiple conditions efficiently and learn modern switch patterns
Content by: Akash Vadher
.Net Developer
Switch statements provide an alternative to if...else chains when you need to compare a single value against multiple possible values. They are more readable and often more efficient than long if...else chains.
// Basic switch statement
let day = 'Monday';
switch (day) {
case 'Monday':
console.log('Start of the work week');
break;
case 'Tuesday':
console.log('Second day of the week');
break;
case 'Wednesday':
console.log('Middle of the week');
break;
case 'Thursday':
console.log('Almost Friday');
break;
case 'Friday':
console.log('TGIF!');
break;
case 'Saturday':
case 'Sunday':
console.log('Weekend!');
break;
default:
console.log('Invalid day');
}
// Switch with numbers
let grade = 85;
switch (true) {
case grade >= 90:
console.log('Grade: A');
break;
case grade >= 80:
console.log('Grade: B');
break;
case grade >= 70:
console.log('Grade: C');
break;
case grade >= 60:
console.log('Grade: D');
break;
default:
console.log('Grade: F');
}
// Switch with expressions
let userType = 'premium';
let discount = 0;
switch (userType) {
case 'admin':
discount = 100;
break;
case 'premium':
discount = 25;
break;
case 'regular':
discount = 10;
break;
case 'guest':
discount = 5;
break;
default:
discount = 0;
}
console.log(`Discount: ${discount}%`);
// Switch without break (fall-through)
let month = 2;
let daysInMonth;
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
daysInMonth = 31;
break;
case 4:
case 6:
case 9:
case 11:
daysInMonth = 30;
break;
case 2:
daysInMonth = 28; // Simplified, ignoring leap years
break;
default:
daysInMonth = 0;
}
console.log(`Days in month ${month}: ${daysInMonth}`);
// Modern switch patterns (2025)
// Switch with object mapping
const actionHandlers = {
'create': (data) => console.log('Creating:', data),
'read': (id) => console.log('Reading:', id),
'update': (id, data) => console.log('Updating:', id, data),
'delete': (id) => console.log('Deleting:', id)
};
function handleAction(action, ...args) {
const handler = actionHandlers[action];
if (handler) {
handler(...args);
} else {
console.log('Unknown action:', action);
}
}
handleAction('create', { name: 'John' });
handleAction('read', 123);
handleAction('update', 123, { name: 'Jane' });
handleAction('delete', 123);
// Switch with function calls
function getGreeting(time) {
switch (true) {
case time < 12:
return 'Good morning';
case time < 17:
return 'Good afternoon';
case time < 21:
return 'Good evening';
default:
return 'Good night';
}
}
const currentHour = new Date().getHours();
console.log(getGreeting(currentHour));
// Switch with complex conditions
function getShippingCost(weight, destination) {
let baseCost = 0;
// Base cost by weight
switch (true) {
case weight <= 1:
baseCost = 5;
break;
case weight <= 5:
baseCost = 10;
break;
case weight <= 10:
baseCost = 15;
break;
default:
baseCost = 20;
}
// Multiplier by destination
let multiplier = 1;
switch (destination.toLowerCase()) {
case 'local':
multiplier = 1;
break;
case 'domestic':
multiplier = 1.5;
break;
case 'international':
multiplier = 3;
break;
default:
multiplier = 2;
}
return baseCost * multiplier;
}
console.log('Local shipping (2kg):', getShippingCost(2, 'local'));
console.log('International shipping (8kg):', getShippingCost(8, 'international'));
// Exercise: Build a Calculator
function calculate(operation, a, b) {
switch (operation) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
if (b === 0) {
throw new Error('Division by zero');
}
return a / b;
case '**':
return a ** b;
case '%':
return a % b;
default:
throw new Error('Unknown operation');
}
}
// Exercise: Build a Color Theme System
function getTheme(themeName) {
switch (themeName.toLowerCase()) {
case 'light':
return {
background: '#ffffff',
text: '#000000',
primary: '#007bff',
secondary: '#6c757d'
};
case 'dark':
return {
background: '#121212',
text: '#ffffff',
primary: '#bb86fc',
secondary: '#03dac6'
};
case 'blue':
return {
background: '#e3f2fd',
text: '#1565c0',
primary: '#1976d2',
secondary: '#42a5f5'
};
case 'green':
return {
background: '#e8f5e8',
text: '#2e7d32',
primary: '#388e3c',
secondary: '#66bb6a'
};
default:
return {
background: '#ffffff',
text: '#000000',
primary: '#007bff',
secondary: '#6c757d'
};
}
}
// Test the functions
console.log(calculate('+', 10, 5)); // 15
console.log(calculate('*', 4, 3)); // 12
console.log(calculate('/', 15, 3)); // 5
const lightTheme = getTheme('light');
const darkTheme = getTheme('dark');
console.log('Light theme:', lightTheme);
console.log('Dark theme:', darkTheme);
Test your understanding of this topic:
Master JavaScript loops including for, while, do-while, and modern iteration methods for efficient data processing
Content by: Dharmik Tank
MERN Stack Developer
Loops allow you to execute a block of code multiple times. JavaScript provides several types of loops for different iteration scenarios, from basic counting to complex data processing.
// Basic for loop
for (let i = 0; i < 5; i++) {
console.log(`Iteration ${i}`);
}
// For loop with array
const fruits = ['apple', 'banana', 'orange', 'grape'];
for (let i = 0; i < fruits.length; i++) {
console.log(`Fruit ${i + 1}: ${fruits[i]}`);
}
// For...in loop (for object properties)
const person = {
name: 'John',
age: 30,
city: 'NYC'
};
for (let key in person) {
console.log(`${key}: ${person[key]}`);
}
// For...of loop (for iterable values)
const colors = ['red', 'green', 'blue'];
for (let color of colors) {
console.log(`Color: ${color}`);
}
// For...of with strings
const message = 'Hello';
for (let char of message) {
console.log(`Character: ${char}`);
}
// Nested for loops
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
console.log(`i=${i}, j=${j}`);
}
}
// For loop with break
for (let i = 0; i < 10; i++) {
if (i === 5) {
break; // Exit loop when i equals 5
}
console.log(i);
}
// For loop with continue
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) {
continue; // Skip even numbers
}
console.log(`Odd number: ${i}`);
}
// While loop
let count = 0;
while (count < 5) {
console.log(`Count: ${count}`);
count++;
}
// While loop with condition
let password = 'secret123';
let attempts = 0;
const maxAttempts = 3;
while (attempts < maxAttempts) {
const input = prompt('Enter password:');
if (input === password) {
console.log('Access granted!');
break;
} else {
attempts++;
console.log(`Attempt ${attempts} failed. ${maxAttempts - attempts} attempts remaining.`);
}
}
if (attempts >= maxAttempts) {
console.log('Access denied. Too many failed attempts.');
}
// Do-while loop (executes at least once)
let number = 1;
do {
console.log(`Number: ${number}`);
number *= 2;
} while (number < 10);
// While loop for data processing
let data = [1, 2, 3, 4, 5];
let index = 0;
let sum = 0;
while (index < data.length) {
sum += data[index];
index++;
}
console.log(`Sum: ${sum}`);
// While loop with array modification
let numbers = [1, 2, 3, 4, 5];
let i = 0;
while (i < numbers.length) {
if (numbers[i] % 2 === 0) {
numbers.splice(i, 1); // Remove even numbers
} else {
i++;
}
}
console.log('Odd numbers only:', numbers);
// Array forEach method
const numbers = [1, 2, 3, 4, 5];
numbers.forEach((number, index) => {
console.log(`Number ${index + 1}: ${number}`);
});
// forEach with objects
const users = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
{ name: 'Bob', age: 35 }
];
users.forEach((user, index) => {
console.log(`User ${index + 1}: ${user.name} (${user.age})`);
});
// Map method (creates new array)
const doubled = numbers.map(num => num * 2);
console.log('Doubled numbers:', doubled);
const userNames = users.map(user => user.name);
console.log('User names:', userNames);
// Filter method
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log('Even numbers:', evenNumbers);
const adults = users.filter(user => user.age >= 30);
console.log('Adults:', adults);
// Reduce method
const total = numbers.reduce((sum, num) => sum + num, 0);
console.log('Total:', total);
const oldestUser = users.reduce((oldest, user) =>
user.age > oldest.age ? user : oldest
);
console.log('Oldest user:', oldestUser);
// Find method
const firstEven = numbers.find(num => num % 2 === 0);
console.log('First even number:', firstEven);
const john = users.find(user => user.name === 'John');
console.log('John:', john);
// Some and Every methods
const hasEven = numbers.some(num => num % 2 === 0);
console.log('Has even numbers:', hasEven);
const allAdults = users.every(user => user.age >= 18);
console.log('All adults:', allAdults);
// For...of with different iterables
// Arrays
for (let num of numbers) {
console.log(num);
}
// Strings
for (let char of 'Hello') {
console.log(char);
}
// Sets
const uniqueNumbers = new Set([1, 2, 2, 3, 3, 4]);
for (let num of uniqueNumbers) {
console.log(num);
}
// Maps
const userMap = new Map([
['john', { name: 'John', age: 30 }],
['jane', { name: 'Jane', age: 25 }]
]);
for (let [key, value] of userMap) {
console.log(`${key}: ${value.name}`);
}
// Exercise: Build a Number Guessing Game
function numberGuessingGame() {
const secretNumber = Math.floor(Math.random() * 100) + 1;
let attempts = 0;
const maxAttempts = 10;
console.log('Welcome to the Number Guessing Game!');
console.log('I'm thinking of a number between 1 and 100.');
while (attempts < maxAttempts) {
attempts++;
const guess = parseInt(prompt(`Attempt ${attempts}/${maxAttempts}: Enter your guess:`));
if (isNaN(guess)) {
console.log('Please enter a valid number.');
continue;
}
if (guess === secretNumber) {
console.log(`Congratulations! You guessed it in ${attempts} attempts!`);
return;
} else if (guess < secretNumber) {
console.log('Too low! Try again.');
} else {
console.log('Too high! Try again.');
}
}
console.log(`Game over! The number was ${secretNumber}.`);
}
// Exercise: Build a Data Processing System
function processData(data) {
const results = {
total: 0,
average: 0,
min: Infinity,
max: -Infinity,
evenCount: 0,
oddCount: 0,
positiveCount: 0,
negativeCount: 0
};
// Process each number
for (let num of data) {
results.total += num;
if (num < results.min) results.min = num;
if (num > results.max) results.max = num;
if (num % 2 === 0) {
results.evenCount++;
} else {
results.oddCount++;
}
if (num > 0) {
results.positiveCount++;
} else if (num < 0) {
results.negativeCount++;
}
}
results.average = results.total / data.length;
return results;
}
// Exercise: Build a Pattern Generator
function generatePattern(rows) {
for (let i = 1; i <= rows; i++) {
let pattern = '';
for (let j = 1; j <= i; j++) {
pattern += '* ';
}
console.log(pattern);
}
}
function generateNumberPattern(rows) {
for (let i = 1; i <= rows; i++) {
let pattern = '';
for (let j = 1; j <= i; j++) {
pattern += j + ' ';
}
console.log(pattern);
}
}
// Test the functions
const sampleData = [1, -2, 3, -4, 5, 6, -7, 8, 9, -10];
const stats = processData(sampleData);
console.log('Data Statistics:', stats);
console.log('Star Pattern:');
generatePattern(5);
console.log('Number Pattern:');
generateNumberPattern(5);
Test your understanding of this topic:
Master error handling techniques, try-catch blocks, and debugging strategies for robust JavaScript applications
Content by: Hiren Bhut
MERN Stack Developer
Error handling is crucial for building robust applications. JavaScript provides try-catch blocks and error objects to handle exceptions gracefully and prevent application crashes.
// Basic try-catch
try {
const result = 10 / 0;
console.log(result);
} catch (error) {
console.error('An error occurred:', error.message);
}
// Try-catch with finally
try {
const data = JSON.parse('invalid json');
console.log(data);
} catch (error) {
console.error('JSON parsing error:', error.message);
} finally {
console.log('This always executes');
}
// Multiple catch blocks (not supported in JavaScript)
// Use if-else instead
try {
const result = someRiskyOperation();
console.log(result);
} catch (error) {
if (error instanceof TypeError) {
console.error('Type error:', error.message);
} else if (error instanceof ReferenceError) {
console.error('Reference error:', error.message);
} else {
console.error('Unknown error:', error.message);
}
}
// Async error handling
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch error:', error.message);
return null;
}
}
// Custom error classes
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
class NetworkError extends Error {
constructor(message, statusCode) {
super(message);
this.name = 'NetworkError';
this.statusCode = statusCode;
}
}
// Using custom errors
function validateUser(user) {
if (!user.name) {
throw new ValidationError('Name is required', 'name');
}
if (!user.email) {
throw new ValidationError('Email is required', 'email');
}
if (user.age < 0) {
throw new ValidationError('Age must be positive', 'age');
}
}
try {
validateUser({ name: '', email: 'test@example.com', age: 25 });
} catch (error) {
if (error instanceof ValidationError) {
console.error(`Validation error in ${error.field}: ${error.message}`);
} else {
console.error('Unknown error:', error.message);
}
}
// Error handling patterns
// 1. Error boundary pattern
function withErrorBoundary(operation, fallback) {
try {
return operation();
} catch (error) {
console.error('Operation failed:', error);
return fallback;
}
}
const result = withErrorBoundary(
() => JSON.parse('invalid json'),
{ error: 'Failed to parse data' }
);
// 2. Retry pattern
async function retryOperation(operation, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
console.log(`Attempt ${attempt} failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
// 3. Error logging
class ErrorLogger {
constructor() {
this.errors = [];
}
log(error, context = {}) {
const errorEntry = {
timestamp: new Date(),
error: error.message,
stack: error.stack,
context
};
this.errors.push(errorEntry);
console.error('Error logged:', errorEntry);
// In production, send to error tracking service
this.sendToErrorService(errorEntry);
}
sendToErrorService(errorEntry) {
// Simulate sending to error tracking service
console.log('Sending to error service:', errorEntry);
}
getErrors() {
return this.errors;
}
}
const logger = new ErrorLogger();
// 4. Graceful degradation
function loadFeature(featureName) {
try {
const feature = require(`./features/${featureName}`);
return feature;
} catch (error) {
console.warn(`Feature ${featureName} not available, using fallback`);
return getFallbackFeature(featureName);
}
}
function getFallbackFeature(featureName) {
const fallbacks = {
'advanced-chart': () => ({ render: () => console.log('Basic chart rendered') }),
'real-time-updates': () => ({ start: () => console.log('Polling updates started') })
};
return fallbacks[featureName] || (() => ({ render: () => console.log('Default feature') }));
}
// 5. Input validation
function validateInput(input, schema) {
const errors = [];
for (const [field, rules] of Object.entries(schema)) {
const value = input[field];
if (rules.required && !value) {
errors.push(`${field} is required`);
}
if (value && rules.type && typeof value !== rules.type) {
errors.push(`${field} must be a ${rules.type}`);
}
if (value && rules.min && value < rules.min) {
errors.push(`${field} must be at least ${rules.min}`);
}
if (value && rules.max && value > rules.max) {
errors.push(`${field} must be at most ${rules.max}`);
}
}
if (errors.length > 0) {
throw new ValidationError(errors.join(', '));
}
return true;
}
// Usage
const userSchema = {
name: { required: true, type: 'string' },
age: { required: true, type: 'number', min: 0, max: 120 },
email: { required: true, type: 'string' }
};
try {
validateInput({ name: 'John', age: 25, email: 'john@example.com' }, userSchema);
console.log('Input is valid');
} catch (error) {
console.error('Validation failed:', error.message);
}
// Exercise: Build a Robust Calculator
class SafeCalculator {
constructor() {
this.history = [];
}
add(a, b) {
try {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('Both arguments must be numbers');
}
const result = a + b;
this.history.push({ operation: 'add', a, b, result });
return result;
} catch (error) {
this.logError(error, { operation: 'add', a, b });
throw error;
}
}
divide(a, b) {
try {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('Both arguments must be numbers');
}
if (b === 0) {
throw new Error('Division by zero');
}
const result = a / b;
this.history.push({ operation: 'divide', a, b, result });
return result;
} catch (error) {
this.logError(error, { operation: 'divide', a, b });
throw error;
}
}
sqrt(number) {
try {
if (typeof number !== 'number') {
throw new TypeError('Argument must be a number');
}
if (number < 0) {
throw new Error('Cannot calculate square root of negative number');
}
const result = Math.sqrt(number);
this.history.push({ operation: 'sqrt', number, result });
return result;
} catch (error) {
this.logError(error, { operation: 'sqrt', number });
throw error;
}
}
logError(error, context) {
console.error('Calculator error:', {
message: error.message,
type: error.constructor.name,
context,
timestamp: new Date()
});
}
getHistory() {
return this.history;
}
clearHistory() {
this.history = [];
}
}
// Exercise: Build an API Client with Error Handling
class ApiClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.retryAttempts = 3;
this.retryDelay = 1000;
}
async request(endpoint, options = {}) {
let lastError;
for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
try {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
headers: {
'Content-Type': 'application/json',
...options.headers
},
...options
});
if (!response.ok) {
throw new NetworkError(
`HTTP ${response.status}: ${response.statusText}`,
response.status
);
}
return await response.json();
} catch (error) {
lastError = error;
if (attempt < this.retryAttempts) {
console.log(`Attempt ${attempt} failed, retrying in ${this.retryDelay}ms...`);
await this.delay(this.retryDelay * attempt);
}
}
}
throw lastError;
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async getUsers() {
return this.request('/users');
}
async createUser(userData) {
return this.request('/users', {
method: 'POST',
body: JSON.stringify(userData)
});
}
async updateUser(id, userData) {
return this.request(`/users/${id}`, {
method: 'PUT',
body: JSON.stringify(userData)
});
}
async deleteUser(id) {
return this.request(`/users/${id}`, {
method: 'DELETE'
});
}
}
// Test the error handling
const calculator = new SafeCalculator();
try {
console.log(calculator.add(5, 3)); // 8
console.log(calculator.divide(10, 2)); // 5
console.log(calculator.sqrt(16)); // 4
console.log(calculator.divide(10, 0)); // Error: Division by zero
} catch (error) {
console.error('Calculator error:', error.message);
}
console.log('Calculator history:', calculator.getHistory());
Test your understanding of this topic:
Continue your learning journey and master the next set of concepts.
Continue to Module 3