Introduction to Conditional Statements
Conditional statements are fundamental building blocks in programming that allow your code to make decisions based on certain conditions. They execute different blocks of code depending on whether a specified condition evaluates to true or false.
In JavaScript, there are several types of conditional statements:
- if statement: Executes a block of code if a specified condition is true
- if...else statement: Executes one block if the condition is true and another if it's false
- if...else if...else statement: Tests multiple conditions in sequence
- switch statement: Selects one of many code blocks to execute based on a value comparison
- Conditional (ternary) operator: A shorthand way to write simple conditional expressions
These conditional statements form the foundation of program logic and flow control in JavaScript applications.
Analogy: Conditional Statements as Road Intersections
Think of conditional statements like intersections in a road network:
- An if statement is like a road with a gate that only opens when a condition is met. If the gate is closed, you simply can't proceed down that road.
- An if...else statement is like a fork in the road where you must choose exactly one path.
- An if...else if...else statement is like a complex intersection with multiple possible routes, each with its own sign. You follow the first path whose sign says you're eligible.
- A switch statement is like a roundabout with multiple exits, each clearly labeled with a different destination.
- The ternary operator is like a quick shortcut that immediately branches in one of two directions based on a single condition.
The if Statement
The most basic conditional statement is the if statement. It executes a block of code only if the specified condition evaluates to true.
Basic Syntax
if (condition) {
// Code to be executed if the condition is true
}
How it Works
- The condition is evaluated
- If the condition is truthy, the code inside the block is executed
- If the condition is falsy, the code block is skipped
Simple Examples
// Basic if statement
const age = 18;
if (age >= 18) {
console.log("You are an adult!");
}
// If statement with multiple statements in the block
const score = 85;
if (score >= 70) {
console.log("You passed the test!");
console.log(`Your score is ${score}, which is above passing.`);
}
// Remember: Only truthy conditions execute the block
const userName = ""; // Empty string is falsy
if (userName) {
console.log(`Hello, ${userName}!`); // This won't execute
}
is true?} B -->|Yes| C[Execute code block] B -->|No| D[Skip code block] C --> E[Continue program] D --> E
Truthy and Falsy Values
In JavaScript, conditions aren't limited to boolean expressions. Any value can be used as a condition, and JavaScript will automatically convert it to a boolean.
Falsy values (evaluate to false):
false0(zero)''or""(empty string)nullundefinedNaN(Not a Number)
All other values are truthy, including:
true- Any number other than 0 (including negative numbers)
- Any non-empty string, including
"false"and"0" - All objects and arrays, even empty ones
- All functions
// Examples of truthy/falsy checks
// These will execute the code block
if (true) { console.log("true is truthy"); }
if (42) { console.log("42 is truthy"); }
if ("hello") { console.log("'hello' is truthy"); }
if ([]) { console.log("Empty array is truthy"); }
if ({}) { console.log("Empty object is truthy"); }
if (function(){}) { console.log("Function is truthy"); }
// These will skip the code block
if (false) { console.log("This won't execute"); }
if (0) { console.log("This won't execute"); }
if ("") { console.log("This won't execute"); }
if (null) { console.log("This won't execute"); }
if (undefined) { console.log("This won't execute"); }
if (NaN) { console.log("This won't execute"); }
Real-World Example: Form Validation
In web forms, we often need to validate user input before proceeding:
function validateUsername(username) {
// Check if username exists (not empty)
if (!username) {
return "Username is required.";
}
// Check minimum length
if (username.length < 3) {
return "Username must be at least 3 characters long.";
}
// Check if username contains only allowed characters
if (!/^[a-zA-Z0-9_]+$/.test(username)) {
return "Username can only contain letters, numbers, and underscores.";
}
// If we get here, username is valid
return null; // No error
}
// Usage
const username = "john_doe";
const error = validateUsername(username);
if (error) {
console.log(`Error: ${error}`);
} else {
console.log("Username is valid!");
}
The if...else Statement
The if...else statement extends the basic if statement by providing an alternative code block that executes when the condition is false.
Basic Syntax
if (condition) {
// Code to be executed if the condition is true
} else {
// Code to be executed if the condition is false
}
How it Works
- The condition is evaluated
- If the condition is truthy, the first block is executed
- If the condition is falsy, the second block (after
else) is executed - Exactly one of the blocks will always execute
Simple Examples
// Basic if...else statement
const age = 16;
if (age >= 18) {
console.log("You can vote!");
} else {
console.log("You're too young to vote.");
}
// Another example with a function
function isEven(number) {
if (number % 2 === 0) {
return "Even";
} else {
return "Odd";
}
}
console.log(isEven(42)); // "Even"
console.log(isEven(17)); // "Odd"
is true?} B -->|Yes| C[Execute if block] B -->|No| D[Execute else block] C --> E[Continue program] D --> E
Real-World Example: Checkout Process
In an e-commerce application, conditional logic determines the checkout flow:
function processCheckout(cart, userLoggedIn) {
// Calculate cart total
const total = cart.items.reduce((sum, item) => {
return sum + (item.price * item.quantity);
}, 0);
if (userLoggedIn) {
// Logged-in user checkout process
console.log("Retrieving saved shipping information...");
console.log("Showing payment options including saved methods...");
if (total > 100) {
console.log("Applying free shipping!");
} else {
console.log(`Adding shipping cost of $${calculateShipping(total)}`);
}
return "Proceed to payment with account discount";
} else {
// Guest checkout process
console.log("Requesting shipping information...");
console.log("Showing guest payment options...");
console.log(`Adding shipping cost of $${calculateShipping(total)}`);
return "Proceed to payment as guest";
}
}
function calculateShipping(total) {
// Simple shipping calculation
return total > 50 ? 5.99 : 9.99;
}
// Test with different scenarios
const cart = {
items: [
{ name: "T-shirt", price: 19.99, quantity: 2 },
{ name: "Jeans", price: 49.99, quantity: 1 }
]
};
console.log(processCheckout(cart, true)); // Logged in user
console.log(processCheckout(cart, false)); // Guest user
The if...else if...else Statement
For more complex conditions, you can use the if...else if...else statement to test multiple conditions in sequence.
Basic Syntax
if (condition1) {
// Code to be executed if condition1 is true
} else if (condition2) {
// Code to be executed if condition1 is false and condition2 is true
} else if (condition3) {
// Code to be executed if condition1 and condition2 are false and condition3 is true
} else {
// Code to be executed if all conditions are false
}
How it Works
- Conditions are evaluated in sequence from top to bottom
- The first condition that evaluates to true has its associated block executed
- Once a condition is found to be true and its block is executed, all other conditions are skipped
- The
elseblock is optional and executes only if all conditions are false
Simple Examples
// Grade calculator
const score = 85;
let grade;
if (score >= 90) {
grade = 'A';
} else if (score >= 80) {
grade = 'B';
} else if (score >= 70) {
grade = 'C';
} else if (score >= 60) {
grade = 'D';
} else {
grade = 'F';
}
console.log(`Your grade is ${grade}`); // "Your grade is B"
// Time-based greeting
const hour = new Date().getHours();
let greeting;
if (hour < 6) {
greeting = "Good night";
} else if (hour < 12) {
greeting = "Good morning";
} else if (hour < 18) {
greeting = "Good afternoon";
} else {
greeting = "Good evening";
}
console.log(greeting);
is true?} B -->|Yes| C[Execute first block] B -->|No| D{Condition2
is true?} D -->|Yes| E[Execute second block] D -->|No| F{Condition3
is true?} F -->|Yes| G[Execute third block] F -->|No| H[Execute else block] C --> I[Continue program] E --> I G --> I H --> I
Analogy: if...else if...else as a Series of Checkpoints
Imagine you're trying to get into a club with multiple checkpoints:
- First, they check if you're a VIP (if condition1) - if you are, you can skip the line and enter immediately.
- If you're not a VIP, they check if you have a reservation (else if condition2) - if you do, you can enter through the reservation line.
- If you don't have a reservation, they check if you're on the guest list (else if condition3) - if you are, you can enter but need to show ID first.
- If none of these apply, you have to wait in the regular line (else condition).
Just like in this scenario, in JavaScript's if...else if...else statement, once you satisfy one of the conditions, you don't need to check any further conditions - you've found your entry point.
Real-World Example: Shipping Calculator
A shipping calculator that determines cost based on weight, destination, and method:
function calculateShippingCost(weight, destination, method) {
// Convert weight to kg if in pounds
const weightInKg = weight.unit === 'lb' ? weight.value * 0.453592 : weight.value;
// Base rate per kg
let ratePerKg = 0;
// First determine rate based on destination
if (destination === 'domestic') {
ratePerKg = 5;
} else if (destination === 'continental') {
ratePerKg = 10;
} else if (destination === 'international') {
ratePerKg = 20;
} else {
throw new Error('Invalid destination');
}
// Calculate base cost
let baseCost = weightInKg * ratePerKg;
// Adjust for shipping method
if (method === 'standard') {
// Standard shipping - no adjustment needed
} else if (method === 'express') {
// Express shipping - 50% more expensive
baseCost *= 1.5;
} else if (method === 'overnight') {
// Overnight shipping - 100% more expensive
baseCost *= 2;
} else {
throw new Error('Invalid shipping method');
}
// Apply minimum shipping charge
if (baseCost < 10) {
baseCost = 10;
}
// Round to two decimal places and return
return Math.round(baseCost * 100) / 100;
}
// Test with different scenarios
console.log(calculateShippingCost({ value: 2, unit: 'kg' }, 'domestic', 'standard')); // $10.00
console.log(calculateShippingCost({ value: 5, unit: 'lb' }, 'continental', 'express')); // $34.02
console.log(calculateShippingCost({ value: 1, unit: 'kg' }, 'international', 'overnight')); // $40.00
Nested Conditional Statements
You can place conditional statements inside other conditional statements. This is called "nesting" and allows for more complex decision making.
Basic Syntax
if (condition1) {
// Outer if block
if (condition2) {
// Nested if block
} else {
// Nested else block
}
} else {
// Outer else block
if (condition3) {
// Another nested if block
}
}
Simple Examples
// Simple nested if example
const userAge = 25;
const hasSubscription = true;
if (userAge >= 18) {
console.log("User is an adult");
if (hasSubscription) {
console.log("User has premium access");
} else {
console.log("User has standard access");
}
} else {
console.log("User is a minor");
if (hasSubscription) {
console.log("User has juvenile premium access");
} else {
console.log("User has restricted access");
}
}
// Another example with nested conditions
function canDrive(age, hasLicense, country) {
if (country === 'US') {
if (age >= 16) {
if (hasLicense) {
return "Can drive in the US";
} else {
return "Cannot drive without a license";
}
} else {
return "Too young to drive in the US";
}
} else if (country === 'UK') {
if (age >= 17) {
if (hasLicense) {
return "Can drive in the UK";
} else {
return "Cannot drive without a license";
}
} else {
return "Too young to drive in the UK";
}
} else {
return "Driving laws vary by country";
}
}
is true?} B -->|Yes| C{InnerCondition
is true?} C -->|Yes| D[Execute inner if block] C -->|No| E[Execute inner else block] B -->|No| F{AnotherCondition
is true?} F -->|Yes| G[Execute another inner block] F -->|No| H[Skip another inner block] D --> I[Continue program] E --> I G --> I H --> I
Avoiding Deep Nesting
While nesting is powerful, deep nesting can make code hard to read and maintain. Here are techniques to avoid excessive nesting:
1. Early Returns
// Deeply nested version
function processPayment(amount, user) {
if (amount > 0) {
if (user.account) {
if (user.account.balance >= amount) {
// Process payment
user.account.balance -= amount;
return "Payment successful";
} else {
return "Insufficient funds";
}
} else {
return "No account found";
}
} else {
return "Invalid payment amount";
}
}
// Refactored with early returns
function processPaymentBetter(amount, user) {
// Check invalid conditions first and return early
if (amount <= 0) {
return "Invalid payment amount";
}
if (!user.account) {
return "No account found";
}
if (user.account.balance < amount) {
return "Insufficient funds";
}
// Process payment only if all conditions are met
user.account.balance -= amount;
return "Payment successful";
}
2. Logical Operators
// Nested ifs
if (isLoggedIn) {
if (hasPermission) {
if (resourceExists) {
accessResource();
}
}
}
// Using logical operators
if (isLoggedIn && hasPermission && resourceExists) {
accessResource();
}
3. Extracting Functions
// Nested conditions
function processOrder(order) {
if (order.items.length > 0) {
if (order.paymentMethod) {
if (order.shippingAddress) {
// Process order...
} else {
return "Shipping address required";
}
} else {
return "Payment method required";
}
} else {
return "Cart is empty";
}
}
// Refactored with validation function
function processOrderBetter(order) {
const validationError = validateOrder(order);
if (validationError) {
return validationError;
}
// Process order...
}
function validateOrder(order) {
if (order.items.length === 0) {
return "Cart is empty";
}
if (!order.paymentMethod) {
return "Payment method required";
}
if (!order.shippingAddress) {
return "Shipping address required";
}
return null; // No errors
}
Real-World Example: User Authentication Flow
Authentication systems often require multiple levels of validation:
function authenticateUser(credentials) {
// First check if required fields exist
if (!credentials.username || !credentials.password) {
return {
success: false,
message: "Username and password are required"
};
}
// Find user in database (simulated here)
const user = findUserByUsername(credentials.username);
if (!user) {
// Don't reveal if username exists for security
return {
success: false,
message: "Invalid username or password"
};
}
// Check if account is locked
if (user.accountLocked) {
return {
success: false,
message: "Account is locked. Please contact support."
};
}
// Verify password (simulated)
const passwordMatches = verifyPassword(credentials.password, user.passwordHash);
if (!passwordMatches) {
// Increment failed login attempts (simulated)
incrementFailedLogins(user.id);
// Check if we need to lock account
if (user.failedLoginAttempts + 1 >= 5) {
lockAccount(user.id);
return {
success: false,
message: "Too many failed attempts. Account has been locked."
};
}
return {
success: false,
message: "Invalid username or password"
};
}
// Check if two-factor auth is enabled
if (user.twoFactorEnabled) {
// Generate and send 2FA code (simulated)
const twoFactorCode = generateTwoFactorCode(user.id);
sendTwoFactorCode(user.phone, twoFactorCode);
return {
success: false,
requiresTwoFactor: true,
message: "Please enter the verification code sent to your phone"
};
}
// Authentication successful
// Reset failed attempts and generate session token
resetFailedLogins(user.id);
const token = generateSessionToken(user.id);
return {
success: true,
token: token,
user: {
id: user.id,
username: user.username,
name: user.name,
email: user.email
}
};
}
// Simulated helper functions
function findUserByUsername(username) {
// Simulate database lookup
const users = {
'john_doe': {
id: 123,
username: 'john_doe',
name: 'John Doe',
email: 'john@example.com',
passwordHash: 'hashed_password',
twoFactorEnabled: true,
phone: '+1234567890',
accountLocked: false,
failedLoginAttempts: 0
}
};
return users[username];
}
function verifyPassword(password, hash) {
// In real apps, you'd verify the password against the hash
return password === 'correct_password';
}
function incrementFailedLogins(userId) {
console.log(`Incrementing failed logins for user ${userId}`);
// Would update database in real app
}
function lockAccount(userId) {
console.log(`Locking account for user ${userId}`);
// Would update database in real app
}
function resetFailedLogins(userId) {
console.log(`Resetting failed logins for user ${userId}`);
// Would update database in real app
}
function generateTwoFactorCode(userId) {
// Would generate a random code in real app
return '123456';
}
function sendTwoFactorCode(phone, code) {
console.log(`Sending code ${code} to ${phone}`);
// Would send SMS in real app
}
function generateSessionToken(userId) {
// Would generate a secure token in real app
return 'secure_token_123';
}
// Test authentication
console.log(authenticateUser({
username: 'john_doe',
password: 'wrong_password'
}));
console.log(authenticateUser({
username: 'john_doe',
password: 'correct_password'
}));
The switch Statement
The switch statement provides a way to handle multiple conditions against a single value. It can be more readable than multiple if...else if statements when comparing a value against many possible cases.
Basic Syntax
switch (expression) {
case value1:
// Code to execute if expression === value1
break;
case value2:
// Code to execute if expression === value2
break;
case value3:
// Code to execute if expression === value3
break;
default:
// Code to execute if none of the cases match
break;
}
How it Works
- The
expressionis evaluated once - The resulting value is compared with the values of each
case - If there's a match, the block of code associated with that
caseis executed - The
breakstatement exits the switch block - If no match is found, the
defaultblock (if present) is executed - Without
break, execution "falls through" to the next case
Simple Examples
// Switch based on a string value
const day = new Date().getDay();
let dayName;
switch (day) {
case 0:
dayName = "Sunday";
break;
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
case 4:
dayName = "Thursday";
break;
case 5:
dayName = "Friday";
break;
case 6:
dayName = "Saturday";
break;
default:
dayName = "Invalid day";
break;
}
console.log(`Today is ${dayName}`);
// Equivalent to if...else if...else
function getDayName(day) {
if (day === 0) {
return "Sunday";
} else if (day === 1) {
return "Monday";
} else if (day === 2) {
return "Tuesday";
} // and so on...
}
Fall-Through Behavior
Without a break statement, execution will "fall through" to the next case, which can be useful in some scenarios:
// Using fall-through to handle multiple cases the same way
const month = new Date().getMonth();
let season;
switch (month) {
case 11: // December
case 0: // January
case 1: // February
season = "Winter";
break;
case 2: // March
case 3: // April
case 4: // May
season = "Spring";
break;
case 5: // June
case 6: // July
case 7: // August
season = "Summer";
break;
case 8: // September
case 9: // October
case 10: // November
season = "Fall";
break;
default:
season = "Invalid month";
break;
}
console.log(`The current season is ${season}`);
Switch vs. if...else if
When to use switch instead of if...else if:
- When comparing a single value against multiple possible values
- When you have a large number of possible conditions
- When you want to group multiple values to the same code block using fall-through
When to use if...else if instead of switch:
- When testing different conditions rather than comparing values
- When using complex expressions or different comparison operators
- When the number of conditions is small
Real-World Example: Command Processor
A simple command processor might use a switch statement to handle different commands:
function processCommand(command, args) {
// Parse command and arguments
command = command.toLowerCase().trim();
switch (command) {
case 'help':
displayHelpMenu();
break;
case 'create':
if (!args || args.length === 0) {
console.log("Error: 'create' command requires a name parameter");
console.log("Usage: create [name]");
} else {
createItem(args[0]);
}
break;
case 'list':
// Fall-through for aliases
case 'ls':
case 'show':
listItems(args);
break;
case 'delete':
case 'remove':
if (!args || args.length === 0) {
console.log("Error: 'delete' command requires an id parameter");
console.log("Usage: delete [id]");
} else {
deleteItem(args[0]);
}
break;
case 'edit':
case 'update':
if (!args || args.length < 2) {
console.log("Error: 'edit' command requires id and new value parameters");
console.log("Usage: edit [id] [new value]");
} else {
updateItem(args[0], args[1]);
}
break;
case 'exit':
case 'quit':
console.log("Exiting program...");
return false; // Signal to exit program
default:
console.log(`Unknown command: ${command}`);
console.log("Type 'help' to see available commands");
break;
}
return true; // Continue program execution
}
// Mock implementation of command functions
function displayHelpMenu() {
console.log("Available commands:");
console.log(" help - Display this help menu");
console.log(" create [name] - Create a new item");
console.log(" list - List all items");
console.log(" delete [id] - Delete an item by id");
console.log(" edit [id] [value] - Update an item");
console.log(" exit - Exit the program");
}
function createItem(name) {
console.log(`Creating item: ${name}`);
}
function listItems(args) {
console.log("Listing items:", args ? args.join(', ') : 'all');
}
function deleteItem(id) {
console.log(`Deleting item with id: ${id}`);
}
function updateItem(id, newValue) {
console.log(`Updating item ${id} to: ${newValue}`);
}
// Test the command processor
let running = true;
const testCommands = [
{ cmd: 'help', args: [] },
{ cmd: 'create', args: ['task1'] },
{ cmd: 'ls', args: [] },
{ cmd: 'edit', args: ['1', 'updated task'] },
{ cmd: 'invalid', args: [] },
{ cmd: 'exit', args: [] }
];
for (const test of testCommands) {
console.log(`\n> ${test.cmd} ${test.args.join(' ')}`);
running = processCommand(test.cmd, test.args);
if (!running) {
console.log("Program terminated");
break;
}
}
The Conditional (Ternary) Operator
The conditional (ternary) operator is a shorthand way to write simple if...else statements. It takes three operands: a condition, an expression to execute if the condition is truthy, and an expression to execute if the condition is falsy.
Basic Syntax
condition ? expressionIfTrue : expressionIfFalse
How it Works
- The
conditionis evaluated - If the condition is truthy, the
expressionIfTrueis evaluated and becomes the result - If the condition is falsy, the
expressionIfFalseis evaluated and becomes the result
Simple Examples
// Basic ternary operator
const age = 20;
const status = age >= 18 ? "Adult" : "Minor";
console.log(status); // "Adult"
// Equivalent if...else statement
let status2;
if (age >= 18) {
status2 = "Adult";
} else {
status2 = "Minor";
}
// Using with template literals
const greeting = `Good ${new Date().getHours() < 12 ? "morning" : "afternoon"}`;
console.log(greeting);
// Returning values in functions
function getAbsoluteValue(number) {
return number >= 0 ? number : -number;
}
console.log(getAbsoluteValue(-5)); // 5
Nested Ternaries
You can nest ternary operators, but this can quickly become hard to read:
// Simple nested ternary
const score = 85;
const grade = score >= 90 ? 'A' :
score >= 80 ? 'B' :
score >= 70 ? 'C' :
score >= 60 ? 'D' : 'F';
console.log(grade); // 'B'
// Improved readability with formatting
const grade2 = score >= 90 ? 'A'
: score >= 80 ? 'B'
: score >= 70 ? 'C'
: score >= 60 ? 'D'
: 'F';
// Often more readable with if...else for complex conditions
function getGrade(score) {
if (score >= 90) return 'A';
if (score >= 80) return 'B';
if (score >= 70) return 'C';
if (score >= 60) return 'D';
return 'F';
}
Best Practices
- Use ternary operators for simple conditions
- Avoid nesting more than one level deep
- Use parentheses for clarity when needed
- Format complex ternaries with line breaks and indentation
- Use if...else for complex logic instead of deeply nested ternaries
Real-World Example: UI Component
Ternary operators are very common in front-end frameworks for conditional rendering:
// Simple button component with status-based styling
function renderButton(text, isEnabled, isLoading) {
// Determine class based on state
const className = isLoading ? 'btn-loading' :
!isEnabled ? 'btn-disabled' :
'btn-enabled';
// Determine button text based on state
const buttonText = isLoading ? 'Processing...' : text;
// Determine icon based on state
const icon = isLoading ? 'spinner' :
!isEnabled ? 'lock' :
'check';
// Create button HTML
return `
<button class="btn ${className}" ${!isEnabled && !isLoading ? 'disabled' : ''}>
${isLoading ? '<span class="loading-spinner"></span>' : ''}
<i class="icon-${icon}"></i>
${buttonText}
</button>
`;
}
// Test button component in different states
console.log(renderButton('Submit', true, false)); // Enabled
console.log(renderButton('Submit', false, false)); // Disabled
console.log(renderButton('Submit', true, true)); // Loading
Best Practices for Conditional Statements
1. Keep Conditions Simple
// Avoid complex conditions
if (user.age >= 18 && user.country === 'US' && user.verifiedEmail && !user.isBlocked) {
// Allow access
}
// Better - extract into meaningful functions or variables
const isAdult = user.age >= 18;
const isFromUS = user.country === 'US';
const hasVerifiedEmail = user.verifiedEmail;
const isNotBlocked = !user.isBlocked;
if (isAdult && isFromUS && hasVerifiedEmail && isNotBlocked) {
// Allow access
}
2. Watch Out for Potential Errors
// Risky - might cause "Cannot read property 'name' of undefined"
if (user.profile.name === 'Admin') {
// Grant admin access
}
// Safer - check for existence first
if (user && user.profile && user.profile.name === 'Admin') {
// Grant admin access
}
// Even better with optional chaining (ES2020)
if (user?.profile?.name === 'Admin') {
// Grant admin access
}
3. Use Guard Clauses
// Without guard clauses
function processOrder(order) {
if (order) {
if (order.items.length > 0) {
if (order.paymentVerified) {
// Process the order
return "Order processed successfully";
} else {
return "Payment not verified";
}
} else {
return "No items in order";
}
} else {
return "Invalid order";
}
}
// With guard clauses
function processOrderBetter(order) {
if (!order) {
return "Invalid order";
}
if (order.items.length === 0) {
return "No items in order";
}
if (!order.paymentVerified) {
return "Payment not verified";
}
// Process the order
return "Order processed successfully";
}
4. Avoid Assignments in Conditions
// Bad practice - easy to confuse with equality check
if (user = getUser()) {
// Do something with user
}
// Better practice
const user = getUser();
if (user) {
// Do something with user
}
5. Be Careful with Implicit Type Conversions
// Risky - relies on type coercion
if (userId == 123) {
// User with id 123
}
// Better - explicit comparison
if (userId === 123) {
// User with id 123
}
6. Avoid Nested Conditionals
// Instead of nested if statements
if (condition1) {
if (condition2) {
if (condition3) {
// Do something
}
}
}
// Use logical operators
if (condition1 && condition2 && condition3) {
// Do something
}
7. Consider Using Objects or Maps Instead of switch
// Traditional switch statement
function getColor(fruit) {
switch (fruit.toLowerCase()) {
case 'apple':
return 'red';
case 'banana':
return 'yellow';
case 'grape':
return 'purple';
default:
return 'unknown';
}
}
// Object lookup approach
function getColorBetter(fruit) {
const fruitColors = {
apple: 'red',
banana: 'yellow',
grape: 'purple'
};
return fruitColors[fruit.toLowerCase()] || 'unknown';
}
Real-World Example: Authorization System
A well-structured authorization system using conditional best practices:
// User role-based authorization system
class AuthorizationService {
constructor() {
// Permission definitions
this.permissions = {
'create:article': ['admin', 'editor', 'author'],
'edit:article': ['admin', 'editor', 'author'],
'delete:article': ['admin', 'editor'],
'publish:article': ['admin', 'editor'],
'create:comment': ['admin', 'editor', 'author', 'user'],
'edit:comment': ['admin', 'editor', 'author', 'user'],
'delete:comment': ['admin', 'editor', 'moderator'],
'manage:users': ['admin'],
'view:analytics': ['admin', 'editor']
};
// Role hierarchy
this.roleHierarchy = {
'admin': 100,
'editor': 80,
'moderator': 60,
'author': 40,
'user': 20,
'guest': 0
};
}
// Check if user has a specific permission
hasPermission(user, permission) {
// Guard clause for invalid inputs
if (!user || !permission) {
return false;
}
// Admin has all permissions
if (user.role === 'admin') {
return true;
}
// Check if permission exists
if (!this.permissions[permission]) {
return false;
}
// Check if user's role is in the permitted roles list
return this.permissions[permission].includes(user.role);
}
// Check if user can perform action on a resource
canAccess(user, action, resource, ownerId) {
// Guard clause - no user means guest access only
if (!user) {
user = { role: 'guest' };
}
// Guard clause - if resource doesn't exist, no one can access
if (!resource) {
return false;
}
// Check permission based on action and resource type
const permission = `${action}:${resource}`;
const hasBasePermission = this.hasPermission(user, permission);
// If no base permission, deny access
if (!hasBasePermission) {
return false;
}
// Special case: users can only edit their own content unless they have elevated roles
if (action === 'edit' && ownerId && user.id !== ownerId) {
// Only allow if user role is higher than author
const userLevel = this.roleHierarchy[user.role] || 0;
return userLevel >= this.roleHierarchy['editor'];
}
// Special case: only admins can delete content from other admins
if (action === 'delete' && ownerId) {
const ownerRole = this.getUserRole(ownerId);
if (ownerRole === 'admin' && user.role !== 'admin') {
return false;
}
}
// Base permission check passed
return true;
}
// Mock method to get a user's role
getUserRole(userId) {
// In a real app, this would look up the user in a database
const mockUsers = {
1: 'admin',
2: 'editor',
3: 'author',
4: 'user'
};
return mockUsers[userId] || 'guest';
}
}
// Example usage
const auth = new AuthorizationService();
// Test users
const adminUser = { id: 1, role: 'admin' };
const editorUser = { id: 2, role: 'editor' };
const authorUser = { id: 3, role: 'author' };
const regularUser = { id: 4, role: 'user' };
// Test permissions
console.log("Can admin delete article?", auth.hasPermission(adminUser, 'delete:article')); // true
console.log("Can author delete article?", auth.hasPermission(authorUser, 'delete:article')); // false
console.log("Can user create comment?", auth.hasPermission(regularUser, 'create:comment')); // true
// Test access control
console.log("Can author edit own article?",
auth.canAccess(authorUser, 'edit', 'article', 3)); // true
console.log("Can author edit other's article?",
auth.canAccess(authorUser, 'edit', 'article', 4)); // false
console.log("Can editor edit other's article?",
auth.canAccess(editorUser, 'edit', 'article', 3)); // true
Practical Exercise
Let's practice what we've learned with some exercises about conditional statements.
Exercise 1: Traffic Light Controller
Create a traffic light controller function that determines the next light color:
// Traffic light controller
function getNextLight(currentLight) {
// Convert to lowercase for consistency
currentLight = (currentLight || '').toLowerCase();
// Determine next light
switch (currentLight) {
case 'green':
return 'yellow';
case 'yellow':
return 'red';
case 'red':
return 'green';
default:
return 'Invalid light color';
}
}
// Test cases
console.log(getNextLight('green')); // yellow
console.log(getNextLight('yellow')); // red
console.log(getNextLight('red')); // green
console.log(getNextLight('YELLOW')); // red (case insensitive)
console.log(getNextLight('blue')); // Invalid light color
// Advanced version with pedestrian crossing
function advancedTrafficLight(currentLight, pedestrianWaiting = false) {
// Convert to lowercase for consistency
currentLight = (currentLight || '').toLowerCase();
if (currentLight === 'green') {
// If pedestrian is waiting, change to yellow
return pedestrianWaiting ? 'yellow' : 'green';
} else if (currentLight === 'yellow') {
return 'red';
} else if (currentLight === 'red') {
// Always change red to green
return 'green';
} else {
return 'Invalid light color';
}
}
// Test advanced traffic light
console.log(advancedTrafficLight('green', false)); // green (stays green)
console.log(advancedTrafficLight('green', true)); // yellow (pedestrian waiting)
console.log(advancedTrafficLight('yellow')); // red
Exercise 2: Enhanced Permission System
Create a function that determines if a user can access a specific resource:
// User access control system
function canAccessResource(user, resource) {
// Guard clauses - if user or resource don't exist, deny access
if (!user || !resource) {
return false;
}
// Check if resource is public - anyone can access
if (resource.accessLevel === 'public') {
return true;
}
// If not logged in, deny access to non-public resources
if (!user.isLoggedIn) {
return false;
}
// Check membership level required resources
if (resource.accessLevel === 'member' && user.isMember) {
return true;
}
// Check premium resources
if (resource.accessLevel === 'premium' && user.isPremium) {
return true;
}
// Check if user is the owner of the resource
if (resource.ownerId === user.id) {
return true;
}
// Check if user is an admin (can access everything)
if (user.isAdmin) {
return true;
}
// If none of the above conditions are met, deny access
return false;
}
// Test users
const guestUser = {
isLoggedIn: false
};
const regularUser = {
id: 123,
isLoggedIn: true,
isMember: true,
isPremium: false,
isAdmin: false
};
const premiumUser = {
id: 456,
isLoggedIn: true,
isMember: true,
isPremium: true,
isAdmin: false
};
const adminUser = {
id: 789,
isLoggedIn: true,
isMember: true,
isPremium: true,
isAdmin: true
};
// Test resources
const resources = [
{ id: 1, name: 'Public Article', accessLevel: 'public', ownerId: 123 },
{ id: 2, name: 'Member Tutorial', accessLevel: 'member', ownerId: 456 },
{ id: 3, name: 'Premium Course', accessLevel: 'premium', ownerId: 789 },
{ id: 4, name: 'Regular User Content', accessLevel: 'member', ownerId: 123 }
];
// Test access control
console.log('Guest access to public article:',
canAccessResource(guestUser, resources[0])); // true
console.log('Guest access to member tutorial:',
canAccessResource(guestUser, resources[1])); // false
console.log('Regular user access to premium course:',
canAccessResource(regularUser, resources[2])); // false
console.log('Regular user access to own content:',
canAccessResource(regularUser, resources[3])); // true
console.log('Premium user access to premium course:',
canAccessResource(premiumUser, resources[2])); // true
console.log('Admin access to any content:',
canAccessResource(adminUser, resources[2])); // true
Exercise 3: Shopping Cart Discount Calculator
Create a function that calculates discounts based on various conditions:
// Shopping cart discount calculator
function calculateTotalWithDiscount(cart, couponCode, userType) {
// Guard clause for empty cart
if (!cart || !cart.items || cart.items.length === 0) {
return {
subtotal: 0,
discount: 0,
total: 0,
message: "Cart is empty"
};
}
// Calculate subtotal
let subtotal = 0;
for (const item of cart.items) {
subtotal += item.price * item.quantity;
}
// Initialize discount variables
let discountAmount = 0;
let discountMessage = null;
// Check for product-specific discounts
cart.items.forEach(item => {
// Buy one get one free for eligible products
if (item.eligibleForBOGO && item.quantity >= 2) {
const freeItems = Math.floor(item.quantity / 2);
const itemDiscount = freeItems * item.price;
discountAmount += itemDiscount;
discountMessage = discountMessage || `BOGO applied to ${item.name}`;
}
// Clearance items get additional 10% off
if (item.clearance) {
const clearanceDiscount = item.price * item.quantity * 0.10;
discountAmount += clearanceDiscount;
discountMessage = discountMessage
? `${discountMessage}, Clearance discount applied`
: 'Clearance discount applied';
}
});
// Apply coupon codes
if (couponCode) {
switch (couponCode.toUpperCase()) {
case 'SAVE10':
// 10% off entire order
const coupon10Discount = subtotal * 0.10;
if (coupon10Discount > discountAmount) {
discountAmount = coupon10Discount;
discountMessage = '10% off coupon applied';
}
break;
case 'SAVE20':
// 20% off orders over $100
if (subtotal >= 100) {
const coupon20Discount = subtotal * 0.20;
if (coupon20Discount > discountAmount) {
discountAmount = coupon20Discount;
discountMessage = '20% off coupon applied';
}
} else {
discountMessage = 'SAVE20 requires $100 minimum purchase';
}
break;
case 'FREESHIP':
// Free shipping handled elsewhere
discountMessage = discountMessage
? `${discountMessage}, Free shipping applied`
: 'Free shipping applied';
break;
default:
discountMessage = discountMessage
? `${discountMessage}, Invalid coupon code`
: 'Invalid coupon code';
}
}
// Apply user type discounts
if (userType) {
let userDiscount = 0;
if (userType === 'premium') {
// Premium members get 15% off
userDiscount = subtotal * 0.15;
} else if (userType === 'member') {
// Regular members get 5% off
userDiscount = subtotal * 0.05;
} else if (userType === 'employee') {
// Employees get 30% off
userDiscount = subtotal * 0.30;
}
// Use the better discount
if (userDiscount > discountAmount) {
discountAmount = userDiscount;
discountMessage = `${userType} discount applied`;
}
}
// Calculate final total
const total = Math.max(0, subtotal - discountAmount);
// Round to two decimal places
return {
subtotal: parseFloat(subtotal.toFixed(2)),
discount: parseFloat(discountAmount.toFixed(2)),
total: parseFloat(total.toFixed(2)),
message: discountMessage || "No discounts applied"
};
}
// Test the discount calculator
const cart = {
items: [
{
name: "T-shirt",
price: 19.99,
quantity: 2,
eligibleForBOGO: true,
clearance: false
},
{
name: "Jeans",
price: 49.99,
quantity: 1,
eligibleForBOGO: false,
clearance: false
},
{
name: "Clearance Socks",
price: 4.99,
quantity: 3,
eligibleForBOGO: false,
clearance: true
}
]
};
console.log(calculateTotalWithDiscount(cart)); // No discounts except built-in
console.log(calculateTotalWithDiscount(cart, 'SAVE10')); // 10% coupon
console.log(calculateTotalWithDiscount(cart, 'SAVE20')); // 20% coupon (over $100)
console.log(calculateTotalWithDiscount(cart, 'INVALID')); // Invalid coupon
console.log(calculateTotalWithDiscount(cart, 'SAVE10', 'premium')); // Premium member
Key Takeaways
- Conditional statements are fundamental tools for decision-making in JavaScript
- The
ifstatement executes code only when a condition is true - The
if...elsestatement provides an alternative when the condition is false - The
if...else if...elsestructure allows testing multiple conditions in sequence - Nested conditionals allow for complex decision trees but can reduce readability
- The
switchstatement is useful when comparing a single value against multiple possible values - The conditional (ternary) operator provides a concise syntax for simple if-else statements
- Best practices include keeping conditions simple, using guard clauses, and avoiding deep nesting
- Understanding truthy and falsy values is essential for writing effective conditions