JavaScript Basics II
Conditionals, Loops
& Functions
Building on your JavaScript foundation to control program flow and create
reusable code.
This section delves into the fundamental constructs that allow your
JavaScript programs to make decisions, perform repetitive tasks
efficiently, and organize code into modular, reusable blocks. Mastering
these concepts is crucial for writing dynamic, interactive, and maintainable
web applications.
Conditionals: Making Decisions
Conditionals allow your code to execute different blocks based on whether
a specified condition evaluates to true or false. They are the backbone of
any dynamic program.
1. If, Else If, Else Statements
The most common way to implement conditional logic. The code inside an
if block executes only if its condition is true. You can chain else if
statements for multiple conditions, and an optional else block acts as a
fallback.
2. Switch Statements
A switch statement is a more efficient way to handle multiple else if
conditions that are checking the same variable for different values. It's
often cleaner for multiple fixed conditions.
Loops: Repeating Actions
Loops are used to execute a block of code repeatedly until a certain
condition is met. They are essential for processing lists of data, rendering
UI elements, and much more.
1. For Loop
The for loop is ideal when you know the exact number of iterations
needed.
// Example: Counting from 0 to 4
for (let i = 0; i < 5; i++) { // Initialize i to 0; continue as long as i is less
than 5; increment i after each iteration
[Link]("Count:", i); // Output the current value of i
}
// Example: Iterating over an array
const fruits = ["Apple", "Banana", "Cherry"];
for (let i = 0; i < [Link]; i++) { // Loop from index 0 to length-1 of
the array
[Link]("Fruit:", fruits[i]); // Access array element using index
}
2. While Loop
A while loop executes as long as a specified condition remains true. It's
suitable when the number of iterations is not known beforehand, and
depends on the condition.
let count = 0; // Initialize counter
while (count < 3) { // Continue looping as long as count is less than 3
[Link]("While loop count:", count); // Output the current count
count++; // Increment count to eventually make the condition false
and exit the loop
}
3. Do...While Loop
Similar to while, but guarantees that the loop body executes at least once
before checking the condition.
let input;
do {
input = prompt("Enter 'yes' to continue:"); // Prompt user for input
} while (input !== "yes"); // Continue looping until the user enters "yes"
[Link]("You entered 'yes'!");
Common Pitfall: Forgetting to update the loop condition variable (e.g.,
`count++` in a while loop) can lead to an infinite loop, crashing your
program or browser tab.
4. For...of Loop (for iterables)
Used to iterate over iterable objects like arrays, strings, Maps, Sets, etc.,
directly accessing the value of each element.
const colors = ["Red", "Green", "Blue"];
for (const color of colors) { // Iterate over each 'color' value in the
'colors' array
[Link]("Color:", color);
}
const message = "Hello";
for (const char of message) { // Iterate over each character in the
'message' string
[Link]("Character:", char);
}
5. For...in Loop (for object properties)
Used to iterate over the enumerable properties (keys) of an object.
const person = { name: "Alice", age: 30, city: "New York" };
for (const key in person) { // Iterate over each 'key' (property name) in
the 'person' object
[Link](`${key}: ${person[key]}`); // Access property value using
bracket notation
}
Best Practice: Prefer for...of for arrays and other iterables, and
[Link](), [Link](), or [Link]() with forEach() or a for...of
loop for objects, especially to avoid iterating over inherited properties.
Functions: Reusable Code Blocks
Functions are blocks of code designed to perform a particular task. They
help organize your code, make it reusable, and improve readability.
1. Function Declarations
The traditional way to define a named function. They are "hoisted,"
meaning you can call them before they are declared in the code.
function greet(name) { // Define a function named 'greet' that takes
one parameter 'name'
return `Hello, ${name}!`; // Return a greeting string
}
[Link](greet("World")); // Call the function and log its return value
[Link](greet("Alice"));
2. Function Expressions
Functions can also be defined as expressions and assigned to a variable.
They are not hoisted, so you must define them before calling.
const add = function(a, b) { // Define an anonymous function and
assign it to the 'add' constant
return a + b; // Return the sum of a and b
};
[Link](add(5, 3)); // Call the function via the variable
3. Arrow Functions (ES6+)
A concise way to write function expressions, especially useful for short,
single-line functions or when maintaining the context of this is important.
const multiply = (x, y) => x * y; // Concise arrow function: takes x, y;
returns their product
[Link](multiply(4, 6));
const sayHi = () => [Link]("Hi there!"); // Arrow function with no
parameters
sayHi();
4. Parameters and Return Values
Parameters: Variables listed inside the parentheses in the function
definition. They act as placeholders for values passed into the function
when it's called.
Arguments: The actual values passed to the function when it's invoked.
Return Value: A function can return a value using the return keyword. If
no return statement is specified, or if return; is used without a value,
the function implicitly returns undefined.
function calculateArea(length, width) { // 'length' and 'width' are
parameters
if (length <= 0 || width <= 0) {
return "Invalid dimensions"; // Early exit with an error message
}
return length * width; // Returns the calculated area
}
let roomArea = calculateArea(10, 5); // 10 and 5 are arguments
[Link]("Room Area:", roomArea); // Output: Room Area: 50
let invalidArea = calculateArea(-2, 5);
[Link]("Invalid Area:", invalidArea); // Output: Invalid Area: Invalid
dimensions
5. Scope (Local vs. Global)
Scope determines where variables are accessible. Variables declared
inside a function are local to that function and cannot be accessed from
outside. Variables declared outside any function are global and accessible
from anywhere.
let globalVar = "I'm global!"; // Global variable
function demonstrateScope() {
let localVar = "I'm local to demonstrateScope!"; // Local variable
[Link](globalVar); // Can access globalVar
[Link](localVar); // Can access localVar
}
demonstrateScope();
// [Link](localVar); // This would cause an error: localVar is not
defined
Best Practice: Aim for functions that are focused on a single
responsibility. This makes them easier to understand, test, and reuse. Give
functions descriptive names that clearly indicate their purpose.
Troubleshooting Tip: If a function isn't producing the expected output,
use [Link]() statements inside the function to inspect the values of
its parameters and any intermediate variables at different stages of
execution.
What We Covered in Basics I
Variables & Data Types Operators Template Literals
Variables are containers for storing Operators are special symbols that Template literals (introduced in
data values. In JavaScript, you perform operations on one or more ES6/ES2015) offer a powerful and
declare variables using let, const, or values (operands). They are crucial readable way to work with strings.
(less commonly) var. Data types for performing calculations, making They are defined using backticks (` `)
classify the kind of values a variable comparisons, and controlling instead of single or double quotes,
can hold, influencing how they program flow. We covered several and provide two main advantages
behave and what operations can be categories: over traditional string concatenation:
performed on them. Key primitive Arithmetic Operators: For 1. String Interpolation: Embed
data types include:
mathematical calculations (+, -, *, expressions directly within string
Strings: Textual data (e.g., "Hello, /, % for remainder, ** for literals using ${expression}. This
World!"). exponentiation). makes building dynamic strings
Numbers: Both integers and Assignment Operators: Assign much cleaner.
floating-point values (e.g., 10, values to variables (=, +=, -=, *=, 2. Multi-line Strings: You can write
3.14). /=, etc.). strings that span multiple lines
Booleans: Represent truth values Comparison Operators: without needing special escape
(true or false). Compare two values and return a characters (like \n).
Null: Represents the intentional boolean (==, ===, !=, !==, >, <, >=,
absence of any object value. <=). let itemName = "Laptop";
let itemPrice = 1200;
Undefined: Indicates a variable Logical Operators: Combine or
let taxRate = 0.08;
has been declared but not yet negate boolean expressions (&&
assigned a value. for AND, || for OR, ! for NOT).
// Traditional string
Symbol: Unique identifiers. Unary Operators: Operate on a
concatenation (less readable for
single operand (++ for increment,
BigInt: For very large integer complex strings)
-- for decrement).
numbers. let messageOld = "The " +
itemName + " costs $" +
// Declaring variables with let quantity = 5; itemPrice + ". Taxes are " +
different data types let pricePerItem = 20; (itemPrice * taxRate).toFixed(2) +
let productName = "Wireless ".";
Headphones"; // String // Arithmetic Operator // Output: "The Laptop costs
const productPrice = 99.99; // let totalCost = quantity * $1200. Taxes are 96.00."
Number (float) pricePerItem; // 5 * 20 = 100
let inStock = true; // Boolean // Using Template Literals for
let discountPercentage = null; // // Assignment Operator string interpolation
Null (intentional absence of totalCost += 10; // totalCost = let messageNew = `The
value) totalCost + 10; -> 110 ${itemName} costs
let futureFeature; // Undefined $${itemPrice}. Taxes are
(declared, no value assigned) // Comparison Operators ${(itemPrice *
let isExpensive = totalCost > 100; taxRate).toFixed(2)}.`;
// Practical Tip: // true // Output: "The Laptop costs
// Use 'const' for variables whose let isEqual = (quantity == '5'); // $1200. Taxes are 96.00."
values should not be reassigned. true (loose equality, type
// Use 'let' for variables whose coercion) // Multi-line string with template
values might change. let isStrictlyEqual = (quantity === literals
// Avoid 'var' due to its confusing '5'); // false (strict equality, no let productDescription = `
scoping rules. type coercion) Product: ${itemName}
Price: $${itemPrice}
// Common Pitfall: // Logical Operators Availability: In Stock
// Misunderstanding 'const' with let hasDiscount = true; `;
objects/arrays. 'const' prevents let canPurchase = isExpensive // This string will retain its line
reassignment of the variable, && hasDiscount; // true && true breaks in the output.
// but it does NOT prevent = true
modification of the object/array // Practical Tip:
content itself. // Unary Operators // Use template literals
const user = { name: "Alice" }; quantity++; // quantity is now 6 whenever you need to combine
[Link] = "Bob"; // This is let negatedCondition = variables or expressions
allowed !isExpensive; // false // with static text in a string. It
// user = { name: "Charlie" }; // greatly improves readability and
This would cause an error // Best Practice: maintainability.
(reassignment) // Always use '===' (strict
equality) and '!==' (strict // Common Pitfall:
inequality) // Forgetting to use backticks (`)
// to avoid unexpected type and instead using single or
Real-world context: You'd use
coercion behavior of '==' and '!='. double quotes.
strings for product names, customer
names, or descriptions; numbers for // This will prevent interpolation
// Common Pitfall: from working, treating ${} as
prices, quantities, or IDs; and
// Confusing '=' (assignment) plain text.
booleans for checking if an item is
with '==' or '===' (comparison) in // Example: "The item costs
available or if a user is logged in.
conditional statements. ${itemPrice}." // Output: "The
// This can lead to bugs where a item costs ${itemPrice}."
condition always evaluates to
true or false.
Real-world context: Template
literals are ideal for generating
Real-world context: Operators are dynamic content in user interfaces,
fundamental for everything from creating personalized email
calculating discounts and taxes messages, logging information to the
(arithmetic), updating inventory console, or constructing complex
levels (assignment), to validating SQL queries (though be cautious
user input or determining eligibility about SQL injection). They make
for promotions (comparison and your code cleaner and easier to
logical). understand.
Real-world application: In our e-commerce scenario, mastering these basics allowed us to effectively model a product catalog.
We could declare variables to hold product names (strings), prices (numbers), and stock status (booleans). Operators helped us
calculate total costs, apply discounts, and determine if an item was in stock. Finally, template literals became invaluable for
dynamically generating product listings, order confirmations, and user-friendly messages that combined all this varied data into
coherent output, ensuring a functional and interactive user experience.
Making Decisions with
Conditionals
Conditionals are fundamental building blocks in programming that allow
your programs to execute different blocks of code based on whether
certain conditions are met. This capability enables programs to make
"smart" decisions and adapt their behavior to various situations, inputs, or
states.
The most basic conditional statement is the if statement, which you use to
check a condition and run a block of code if that condition is true.
let age = 18; // Declare a variable 'age' and assign it the value 18
// The 'if' statement checks if the condition inside the parentheses is
true.
if (age >= 18) { // Condition: Is 'age' greater than or equal to 18?
[Link]("You can vote!"); // If the condition is true, this code
runs.
}
The if statement evaluates the condition provided within its parentheses. If
this condition evaluates to true, the code inside the subsequent curly
braces {} is executed. If the condition is false, that block of code is entirely
skipped.
Adding Alternatives with else
Often, you'll want to perform an alternative action if the initial if condition
is not met. This is where the else statement comes in handy. It provides a
fallback block of code that runs when the if condition is false.
let temperature = 25; // Define the current temperature
// Check if the temperature is above 30 degrees Celsius
if (temperature > 30) {
[Link]("It's a hot day! Stay hydrated."); // This runs if
temperature is > 30
} else { // If the 'if' condition is false (temperature is 30 or less)
[Link]("The weather is pleasant."); // This code runs instead
}
Handling Multiple Scenarios with else if
When you have several possible conditions to check, and each leads to a
different outcome, you can chain if and else statements together using
else if. This allows your program to test multiple conditions sequentially
until one is true.
let score = 85; // Assume a student's score
// First, check if the score is 90 or above
if (score >= 90) {
[Link]("Grade: A"); // Executed if score is 90+
} else if (score >= 80) { // If not 'A', check if score is 80 or above
[Link]("Grade: B"); // Executed if score is between 80 and 89
} else if (score >= 70) { // If not 'A' or 'B', check if score is 70 or above
[Link]("Grade: C"); // Executed if score is between 70 and 79
} else { // If none of the above conditions are true
[Link]("Grade: F"); // Executed for scores below 70
}
Practical Tips for Conditionals
Use Strict Equality (===): Always prefer === over == when comparing
values. === checks both value and type, preventing unexpected type
coercion issues (e.g., 5 == '5' is true, but 5 === '5' is false).
Readability: Use proper indentation for nested conditionals and clear
variable names to make your code easy to understand.
Order Matters for else if: When using else if chains, place the most
specific or restrictive conditions first. For example, if checking for
ranges, check >= 90 before >= 80.
Common Pitfalls to Avoid
Assignment vs. Comparison: A classic mistake is using a single equals
sign (=) for assignment instead of double or triple equals (== or ===)
for comparison within an if condition. This can lead to unexpected
behavior as assignment operations often evaluate to the assigned
value, which can be truthy.
Missing Curly Braces: While if and else statements can technically
execute a single line of code without curly braces, it's best practice to
always include them. This prevents bugs if you later add more lines of
code to the block.
Real-World Applications
Conditionals are ubiquitous in software development:
User Authentication: Checking if a username and password match
stored credentials before granting access.
Form Validation: Ensuring user input meets specific criteria (e.g., email
format, password strength).
Game Logic: Determining if a player has enough health to survive an
attack, or if an item can be picked up.
E-commerce: Calculating shipping costs based on location, applying
discounts based on cart total, or checking stock availability.
Troubleshooting Conditional Logic
If your conditionals aren't behaving as expected, try these debugging
steps:
[Link](): Place [Link]() statements inside each branch of your
conditional to see which block of code is being executed.
Inspect Variables: Use [Link]() to check the exact values of the
variables being used in your conditions immediately before the if
statement.
Test Conditions Independently: Evaluate your conditions directly in
the console (e.g., age >= 18) to ensure they return the expected true or
false.
Handling Multiple Scenarios with If-Else If-Else
When your program needs to evaluate several distinct possibilities and execute different code blocks based on which condition
is met, the if-else if-else structure is your go-to solution. It provides a clean and efficient way to manage complex decision logic.
If-Else If-Else Structure in Depth How It Works: Sequential Evaluation & Short-
Circuiting
let score = 75; // Define a variable 'score' and assign it a
JavaScript processes if-else if-else statements sequentially
value
from top to bottom:
// Check if the score is 80 or higher 1. The first if condition is checked. If it's true, its block
if (score >= 80) { executes, and the entire structure is exited.
[Link]("Excellent! You scored A."); // This code 2. If the first if condition is false, the first else if condition is
runs if the above condition is true then checked. If it's true, its block executes, and the entire
}
structure is exited.
// Otherwise, if the score is 50 or higher
3. This process continues for all subsequent else if
else if (score >= 50) {
conditions.
[Link]("Pass! You scored C."); // This code runs if
the first condition is false AND this one is true 4. If all if and else if conditions evaluate to false, the final else
} block (if present) is executed as a fallback.
// If none of the above conditions are true
In the initial score = 75 example:
else {
[Link]("Fail! You scored F."); // This code runs if all 1. score >= 80 (75 >= 80) is false.
preceding conditions are false 2. score >= 50 (75 >= 50) is true.
}
3. The code inside this else if block ([Link]("Pass!");)
executes.
This structure allows for a series of conditional checks. Once
4. The program then skips any remaining else if or else
a condition evaluates to true, its corresponding code block is
blocks.
executed, and the rest of the else if and else blocks are
skipped. This ensures that only one path is taken. 5. Output: "Pass! You scored C."
Practical Tips & Best Practices
// Another example: Determining user access level
let userRole = "admin"; // Define the user's role Order Matters: Always place your most specific or narrow
conditions first. For instance, if checking age, check for
// Check if the user is an administrator children (age < 13) before checking for teenagers (age <
if (userRole === "admin") { 18) if those ranges have different logic.
[Link]("Full access granted: Manage all settings."); Readability: Use clear variable names and proper
// Executed for admins indentation to make your conditional logic easy to
} understand.
// Else, if the user is an editor
Default Case: It's good practice to include a final else
else if (userRole === "editor") {
block to handle any cases not explicitly covered by your if
[Link]("Limited access: Edit content only."); //
or else if conditions. This prevents unexpected behavior
Executed for editors
for unhandled inputs.
}
// Else, if the user is a viewer Avoid Deep Nesting: While possible, deeply nested if
else if (userRole === "viewer") { statements (an if inside another if) can become hard to
[Link]("Read-only access: View content."); // read and maintain. Consider flattening your logic with else
Executed for viewers if or using helper functions.
}
Common Pitfalls & Troubleshooting
// If none of the defined roles match
else { Incorrect Order: As mentioned, reversing the order of
[Link]("Guest access: Please log in."); // Default conditions can lead to bugs. If you check for score >= 50
for unknown roles before score >= 80, a score of 85 would incorrectly trigger
} the "Pass" message and exit.
Missing Braces: Forgetting curly braces {} around code
blocks can cause only the first line after the if or else if to
be conditional, leading to syntax errors or logical bugs.
Assignment vs. Comparison: Using a single equals sign
(=) for assignment instead of double (==) or triple (===)
for comparison within a condition is a common error that
can lead to unexpected truthy/falsy evaluations.
Debugging: If your conditional isn't behaving as expected,
use [Link]() statements to print the values of
variables and which conditional branch is being entered.
This helps trace the execution flow.
Comparison & Logical Operators
Comparison and logical operators are fundamental to controlling the flow of your JavaScript programs. They allow you to
evaluate conditions, make decisions, and execute different code paths based on whether those conditions are true or false.
Understanding their nuances, especially regarding type coercion, is crucial for writing robust and predictable code.
Equality Check Logical Operators
== (Loose Equality) checks if two values are equal after && (Logical AND): Returns true if both operands are true. If
performing type coercion if their types differ. This means the first operand is false, the second is not evaluated
JavaScript might convert one or both values to a common (short-circuiting).
type before comparison.
|| (Logical OR): Returns true if at least one operand is
=== (Strict Equality) checks if two values are equal true. If the first operand is true, the second is not
without performing any type coercion. Both the value and evaluated (short-circuiting).
the type must be identical for the comparison to return
! (Logical NOT): Flips the boolean value of its operand.
true.
Converts an operand to a boolean before flipping it.
Deep Dive into Equality
Let's look at more examples to solidify the difference between loose and strict equality. Understanding type coercion is key to
avoiding unexpected behavior when using ==.
// Loose Equality (==) with type coercion
[Link]("5" == 5); // true (string "5" is converted to number 5)
[Link](0 == false); // true (false is converted to 0)
[Link]("" == false); // true (empty string is converted to 0, false to 0)
[Link](null == undefined); // true (special case, they are loosely equal)
[Link]("1" == true); // true (true is converted to 1, string "1" to number 1)
// Strict Equality (===) without type coercion
[Link]("5" === 5); // false (different types: string vs. number)
[Link](0 === false); // false (different types: number vs. boolean)
[Link]("" === false); // false (different types: string vs. boolean)
[Link](null === undefined); // false (different types: null vs. undefined)
[Link]("1" === true); // false (different types: string vs. boolean)
Best Practice: Always prefer === (strict equality) to == (loose equality) unless you have a very specific reason and fully
understand the implications of type coercion. Strict equality leads to more predictable code and fewer bugs.
Understanding Logical Operators in Action
Logical operators are used to combine multiple boolean expressions or non-boolean values to produce a single boolean result.
They are essential for creating complex conditions in if statements, loops, and other control structures.
let age = 20;
let hasLicense = true;
let isStudent = false;
// Logical AND (&&) - both conditions must be true
if (age > 18 && hasLicense) {
[Link]("Eligible to drive."); // Output: Eligible to drive. (age > 18 is true, hasLicense is true)
}
// Logical OR (||) - at least one condition must be true
if (age < 18 || isStudent) {
[Link]("Discount available."); // No output (age < 18 is false, isStudent is false)
}
// Logical NOT (!) - inverts the boolean value
let isLoggedIn = false;
if (!isLoggedIn) {
[Link]("Please log in."); // Output: Please log in. (!false is true)
}
// Combining operators: checking for eligibility for a special student discount
let isOver18 = age > 18; // true
let hasGoodGrades = true;
if (isStudent && isOver18 && hasGoodGrades) {
[Link]("Eligible for special student discount!"); // No output (isStudent is false)
} else if (isStudent || isOver18) {
[Link]("Standard student or adult discount available."); // Output: Standard student or adult discount available.
(isOver18 is true)
}
Real-World Application: User Authentication and Permissions
The initial code example demonstrates a basic login check. Let's expand on this to include different user roles, showcasing how
comparison and logical operators work together to manage permissions.
let username = "admin";
let password = "securepassword";
let userRole = "administrator"; // Can be 'administrator', 'editor', 'viewer'
let isAuthenticated = false; // Flag to track authentication status
// 1. Check if username and password match for login
if (username === "admin" && password === "securepassword") {
// Both conditions (username matches AND password matches) must be strictly true
[Link]("Login successful!");
isAuthenticated = true; // Set authentication flag to true
} else {
[Link]("Invalid credentials.");
}
// 2. Check if the authenticated user has permission to edit content
if (isAuthenticated && (userRole === "administrator" || userRole === "editor")) {
// User must be authenticated AND (either administrator OR editor)
[Link]("User has editing permissions.");
} else if (isAuthenticated && userRole === "viewer") {
// User is authenticated but only has viewer role
[Link]("User has viewing permissions only.");
} else {
// User is not authenticated or has an unrecognized role
[Link]("Access denied or unknown role.");
}
// 3. Example of a denied action for a non-admin
if (isAuthenticated && !(userRole === "administrator")) {
// If authenticated AND NOT an administrator
[Link]("Non-admin user, cannot perform admin tasks.");
}
Troubleshooting Tip: When a complex conditional doesn't behave as expected, break it down into smaller parts. Use
[Link]() to check the boolean value of each sub-expression. For example, [Link](username === "admin") and
[Link](password === "securepassword") to see which part is failing.
Common Pitfall: Confusing = (assignment operator) with == or === (comparison operators). Using a single equals sign in a
conditional statement will assign a value instead of comparing it, which can lead to unexpected true results (e.g., if (x = 0) assigns
0 to x and then evaluates 0 as false, while if (x == 0) compares).
Switch Statements
A switch statement is a more efficient way to handle multiple else if conditions that are checking the same variable for different
values. It's often cleaner for multiple fixed conditions.
switch executes the code blocks that matches an expression.
switch(expression) {
case x:
// code block
break;
case y:
// code block
break;
default:
// code block
}
This is how it works:
The switch expression is evaluated once.
The value of the expression is compared with the values of each case.
If there is a match, the associated block of code is executed.
If there is no match, no code is executed.
let dayOfWeek = "Wednesday"; // new Date().getDay()
switch (dayOfWeek) {
case "Monday": // If dayOfWeek is "Monday"
[Link]("Start of the work week.");
break; // Exit the switch statement
case "Friday": // If dayOfWeek is "Friday"
[Link]("Almost the weekend!");
break;
case "Saturday": // If dayOfWeek is "Saturday" or "Sunday" (due to fall-through)
case "Sunday":
[Link]("It's the weekend!");
break;
default: // If dayOfWeek doesn't match any case
[Link]("It's a regular weekday.");
}
The break Keyword
When JavaScript reaches a break keyword, it breaks out of the switch block.
This will stop the execution inside the switch block.
No more statements in the switch block will be executed.
It is not necessary to break the last case. The switch ends (breaks) there anyway.
Common Pitfall: Forgetting break; in a case block will lead to "fall-through," where code from subsequent case blocks will
execute until a break or the end of the switch statement is reached. This is sometimes intentional but often a source of bugs.
The default Keyword
The default keyword specifies a block of code to run if there is no case match.
The default keyword is optional.
The default can act as a fallback:
The default case does not have to be the last case in a switch block:
switch (new Date().getDay()) {
default:
text = "Looking forward to the Weekend";
break;
case 6:
text = "Today is Saturday";
break;
case 0:
text = "Today is Sunday";
}
Strict Comparison
Switch uses strict comparison (===).
The values must be of the same type to match.
A strict comparison can only be true if both operands are of the same type.
let x = "0";
switch (x) {
case 0:
text = "Off";
break;
case 1:
text = "On";
break;
default:
text = "No value found";
}
Automating Repetition with
For Loops
Loops save you from writing the same code multiple times. Perfect for
repetitive tasks! They are fundamental control structures that allow a block
of code to be executed repeatedly as long as a certain condition is met.
This makes your code more efficient, readable, and less prone to errors
when dealing with sequential operations.
// Basic 'for' loop to count from 1 to 5
for (let i = 1; i <= 5; i++) { // The loop initializes 'i' to 1, continues as long
as 'i' is less than or equal to 5, and increments 'i' by 1 each time.
[Link]("Count:", i); // This line prints the current value of 'i' in
each iteration.
}
// Output:
// Count: 1
// Count: 2
// Count: 3
// Count: 4
// Count: 5
The loop has three parts: starting value (initialization), condition to
continue (termination check), and increment step (iteration expression).
Let's break these down:
1. Initialization (let i = 1): Executed once at the very beginning of the
loop. It typically declares and initializes a loop counter variable.
2. Condition (i <= 5): Evaluated before each iteration. If it's true, the loop
body executes. If it's false, the loop terminates.
3. Iteration Expression (i++): Executed at the end of each loop iteration.
It typically updates the loop counter, moving towards the termination
condition.
Practical Applications & Real-World Context
For loops are essential in programming for tasks such as:
Iterating over collections: Processing items in an array (e.g., a list of
products, user data).
Generating sequences: Printing numbers, creating patterns, or
building data sets.
Repeating actions: Retrying network requests, performing animations,
or simulating events.
Example: Iterating Through an Array
let fruits = ["apple", "banana", "cherry", "date"]; // Define an array of
fruit names.
// Loop through each element of the 'fruits' array
for (let i = 0; i < [Link]; i++) { // 'i' starts at 0 (first element),
continues as long as 'i' is less than the array's length, and increments
'i' by 1.
[Link]("I like " + fruits[i]); // Accesses the current fruit using its
index 'i' and prints a message.
}
// Output:
// I like apple
// I like banana
// I like cherry
// I like date
Common Pitfalls to Avoid
Off-by-one errors: Be careful with < vs. <= in your condition. For
arrays, using i < [Link] is standard because array indices are 0-
based.
Infinite loops: If your condition never becomes false (e.g., i-- instead of
i++ when i starts at 0 and the condition is i < 5), your program will
freeze.
Modifying loop variable inside the body: Unexpectedly changing the
counter variable i inside the loop body can lead to skipped iterations
or infinite loops.
Best Practices & Troubleshooting Tips
Descriptive variable names: Use meaningful names instead of just i,
especially for nested loops (e.g., rowIndex, colIndex).
Keep it simple: Avoid complex logic within the loop header if possible;
it can make the loop harder to read and debug.
Debugging with [Link]: If your loop isn't behaving as expected,
add [Link]() statements inside the loop to print the values of your
variables (like i, or array elements) at each step. This helps you trace
the execution flow and identify where things go wrong.
Use appropriate loop types: While for loops are versatile, consider
for...of for iterating over iterable objects (like arrays) or forEach array
method for simpler array iteration, which can sometimes be more
readable.
While & Do-While Loops
While Loop: Conditional Execution Do-While Loop: Guaranteed First Execution
The while loop executes a block of code repeatedly as long The do-while loop is similar to the while loop, but with one
as a specified condition is true. It first evaluates the crucial difference: it executes its code block at least once
condition, and if it's true, the code block inside the loop is before evaluating the condition. After the first execution, it
executed. This process repeats until the condition becomes then checks the condition, and if true, repeats the loop. This
false. A key characteristic is that if the condition is initially makes it perfect for scenarios where you need to perform an
false, the loop body will never execute. action once, and then decide whether to repeat it based on a
condition.
let attempts = 0; // Initialize a counter for attempts
let password = ""; // Initialize an empty string for the let userChoice; // Declare a variable to store user input
password input
// Execute the code block at least once
// Loop as long as the password is not "correct" AND do {
attempts are less than 3 userChoice = prompt("Continue? (yes/no)"); // Prompt
while (password !== "correct" && attempts < 3) { the user for a choice
password = prompt("Enter password:"); // Prompt the // Convert input to lowercase for case-insensitive
user for a password comparison
attempts++; // Increment the attempt counter after userChoice = userChoice ? [Link]() :
each try '';
} // Loop as long as the user's choice is neither "yes" nor
// This loop will stop if the password is "correct" OR if "no"
attempts reach 3 or more. } while (userChoice !== "yes" && userChoice !== "no");
// This loop ensures the user provides valid "yes" or "no"
This example simulates a login attempt with a maximum of input.
three tries. If the condition password !== "correct" &&
attempts < 3 is false from the start (e.g., attempts is already In this example, the user will always be prompted at least
3), the loop body will be skipped entirely. once. If their initial input is invalid, they will be prompted
again until "yes" or "no" is entered (case-insensitive due to the
Practical Tips & Common Pitfalls:
toLowerCase() added for robustness).
Use Case: Ideal when the number of iterations is unknown
Practical Tips & Common Pitfalls:
and depends on runtime conditions (e.g., reading data
until end-of-file, polling a server until a certain status is Use Case: Excellent for user input validation, menu-driven
met). programs, or any task that must run at least once before
Infinite Loops: A common pitfall is forgetting to update checking if further iterations are needed.
the variables involved in the loop's condition, leading to an Semicolon: Remember to include the semicolon ; after
infinite loop. Always ensure there's a mechanism within the while (condition) part of the do-while loop. It's a
the loop to eventually make the condition false. common syntax requirement.
Best Practice: Initialize loop control variables before the Troubleshooting: If your loop seems to be running too
loop begins. Carefully design your condition to prevent many times, check your condition. If it runs only once
unintended infinite loops and off-by-one errors. when it should repeat, ensure your condition correctly
evaluates to true for subsequent iterations.
Creating Reusable Code with
Functions
Functions are fundamental building blocks in programming, acting as self-
contained mini-programs designed to perform specific tasks. They
encapsulate a block of code that can be executed on demand, making
your overall program more organized, efficient, and easier to manage.
// Define a simple function called 'greet' that takes one argument:
'name'
function greet(name) {
// The function returns a string that includes the provided name
return `Hello, ${name}! Welcome to our site.`;
}
// Call the 'greet' function with different names and log the returned
value
[Link](greet("Ada")); // Output: "Hello, Ada! Welcome to our
site."
[Link](greet("Grace")); // Output: "Hello, Grace! Welcome to our
site."
At their core, functions take inputs (known as parameters), process them
according to the logic defined within their body, and can optionally
produce an output (a return value). This powerful mechanism allows you
to write a piece of code once and then invoke it multiple times throughout
your program or even in different projects, leading to cleaner, more
maintainable, and less redundant code.
Why Use Functions?
Reusability: Avoid repeating the same code logic in multiple places.
Write it once, call it many times.
Modularity: Break down complex problems into smaller, manageable
sub-problems, each handled by a dedicated function.
Readability: Well-named functions make your code easier to
understand by describing the purpose of a code block.
Maintainability: If a bug is found or a change is needed, you only have
to modify the code in one place (the function definition), rather than
searching for and updating every instance.
Advanced Example: Calculating a Discount
// Function to calculate the final price after applying a discount
function calculateDiscountedPrice(originalPrice, discountPercentage) {
// Check if inputs are valid numbers and discount is within range
if (typeof originalPrice !== 'number' || typeof discountPercentage
!== 'number' || discountPercentage < 0 || discountPercentage > 100)
{
[Link]("Invalid input: Please provide valid numbers for
price and discount (0-100).");
return null; // Return null or throw an error for invalid inputs
}
// Calculate the discount amount
const discountAmount = originalPrice * (discountPercentage / 100);
// Calculate the final price
const finalPrice = originalPrice - discountAmount;
// Return the calculated final price
return finalPrice;
}
// Example usage:
let itemPrice = 120;
let holidayDiscount = 15; // 15% discount
// Call the function and store the result
let priceAfterDiscount = calculateDiscountedPrice(itemPrice,
holidayDiscount);
// Check if the calculation was successful and log the result
if (priceAfterDiscount !== null) {
[Link](`Original Price: $${itemPrice}`);
[Link](`Discount: ${holidayDiscount}%`);
[Link](`Final Price: $${[Link](2)}`); //
Output: Final Price: $102.00
}
// Example with invalid input
calculateDiscountedPrice("abc", 10); // Output: Invalid input: Please
provide valid numbers for price and discount (0-100).
Practical Tips & Best Practices
Descriptive Naming: Give your functions clear, concise names that
accurately describe what they do (e.g., calculateTotal,
displayUserMessage).
Single Responsibility Principle: Each function should ideally do one
thing and do it well. Avoid functions that try to accomplish too many
unrelated tasks.
Keep it Small: Aim for functions that are relatively short. If a function
becomes too long, consider breaking it down into smaller helper
functions.
Pure Functions: Where possible, write functions that, given the same
inputs, always return the same output and produce no side effects (i.e.,
they don't modify anything outside their scope).
Common Pitfalls to Avoid
Forgetting return: If a function is meant to produce a value, ensure it
uses the return keyword. Without it, the function will implicitly return
undefined.
Global Variable Overuse: While functions can access global variables,
relying too heavily on them can lead to unpredictable behavior and
make debugging difficult. Pass necessary data as parameters instead.
Incorrect Parameter Order/Types: Always be mindful of the order
and expected data types of your function parameters. Mismatches can
cause errors or unexpected results.
Lack of Error Handling: For functions that process user input or
external data, add validation and error handling (as shown in the
calculateDiscountedPrice example) to make them robust.
Real-World Applications
E-commerce Checkout Blog Post Display Inventory Management
Functions are invaluable for handling the When managing content-rich websites In inventory management systems,
complex logic involved in e-commerce like blogs, functions are essential for functions are crucial for conditional logic,
checkout processes. Instead of dynamic content rendering. Instead of such as checking stock levels and
repeating code for every product or writing separate HTML for each blog displaying appropriate messages or
every step, you can encapsulate post, you can create a single function actions. Functions can centralize the
calculations like total price, discount that takes post data as input and business rules for stock status, making it
application, and tax computation into outputs a formatted HTML structure. easy to apply these rules consistently
well-defined functions. This ensures This allows you to easily display any across different parts of an application,
consistency, reduces errors, and makes number of posts fetched from a from product pages to order processing.
updates much easier. database or API, ensuring a consistent
look and feel across your entire blog. // Function to determine product
// Function to calculate subtotal of stock status
items // Function to format a single blog function
function calculateSubtotal(items) { post for display getStockStatus(currentStock,
let subtotal = 0; function formatBlogPost(post) { lowStockThreshold = 5) {
for (let item of items) { // Check if the post object has if (currentStock === 0) {
subtotal += [Link] * required properties return "Out of Stock"; // Product
[Link]; // Sum price * if (!post || ![Link] || is completely unavailable
quantity for each item ![Link] || ![Link]) { } else if (currentStock <=
} return ` lowStockThreshold) {
return subtotal; return "Low Stock!"; // Warn
} Error: Invalid post data. about limited availability
} else {
`; } // Return HTML string for the
// Function to apply a discount return "In Stock"; // Available for
post return `
based on a code purchase
function applyDiscount(subtotal,
discountCode) {
${[Link]} }
}
let discountAmount = 0; By ${[Link]} on ${new
if (discountCode === "SAVE10") { Date([Link]).toLocaleDateString()} // Function to update UI based on
discountAmount = subtotal * stock status
${[Link]}
0.10; // 10% discount function
} else if (discountCode === Read More updateProductUI(productId,
"FREESHIP") { stockQuantity) {
`; } // Dummy array of blog post data
// Assume free shipping is const status =
const blogPosts = [ { id: 1, title:
handled elsewhere or is a flat rate getStockStatus(stockQuantity); // Get
"Learning JavaScript Functions", author:
reduction status using our function
"Jane Doe", date: "2023-10-26", content:
discountAmount = 5; // const productElement =
"Functions are fundamental building
Example: $5 off for free shipping [Link](`product-
blocks..." }, { id: 2, title: "Tips for Clean
code ${productId}`); // Find product in UI
Code", author: "John Smith", date: "2023-
}
10-20", content: "Writing readable and
return [Link](discountAmount, if (productElement) {
maintainable code is crucial..." } ]; // Get
subtotal); // Ensure discount doesn't const stockStatusElement =
the container where posts will be
exceed subtotal [Link](".stoc
displayed const postsContainer =
} k-status");
[Link]("blog-posts- const addToCartButton =
// Function to calculate tax container"); // Loop through the posts [Link](".add
function calculateTax(amount, and append their formatted HTML to the -to-cart-button");
taxRate) { container if (postsContainer) { for (let
return amount * taxRate; // post of blogPosts) { if (stockStatusElement) {
Simple tax calculation [Link] +=
} formatBlogPost(post); // Render each [Link] =
post } } else { [Link]("Blog `Status: ${status}`; // Display status
// Example usage: posts container not found!"); } // Apply different styling
const cartItems = [{name: "Shirt", based on status
Practical Tips:
price: 25, quantity: 2}, {name:
"Pants", price: 50, quantity: 1}]; Content Sanitization: If [Link] =
let currentSubtotal = `[Link]` comes from user input, `stock-status
calculateSubtotal(cartItems); // use a sanitization library to prevent ${[Link]().replace(/ /g,
Calculate initial subtotal Cross-Site Scripting (XSS) '-')}`;
let discountApplied = vulnerabilities before rendering. }
applyDiscount(currentSubtotal, Pagination: For a large number of
"SAVE10"); // Apply a discount posts, implement pagination logic if (addToCartButton) {
let totalAfterDiscount = within a function to display only a if (status === "Out of Stock") {
currentSubtotal - discountApplied; // subset of posts at a time, improving [Link]
New total performance. = true; // Disable button if out of
let tax = stock
Common Pitfalls:
calculateTax(totalAfterDiscount,
0.08); // Add 8% tax Direct `innerHTML` Injection: Be [Link] =
let finalTotal = totalAfterDiscount + cautious when using `innerHTML` with "Unavailable";
tax; // Final amount unsanitized user-generated content, } else {
as it can be a security risk. [Link]
[Link](`Subtotal: Performance Issues: Rendering = false; // Enable otherwise
$${[Link](2)}`); hundreds of posts at once can slow
[Link](`Discount: down the page. Optimize by [Link] =
$${[Link](2)}`); rendering only visible posts or using "Add to Cart";
[Link](`Tax: $${[Link](2)}`); virtual scrolling. }
[Link](`Final Total: }
$${[Link](2)}`); }
}
Practical Tips:
// Example products
Parameter Validation: Always
const products = [
validate inputs within your functions
{ id: 101, name: "Laptop", stock: 3
(e.g., check if `items` is an array or if
},
`taxRate` is a valid number) to prevent
{ id: 102, name: "Mouse", stock: 0
unexpected errors.
},
Order of Operations: Be clear about { id: 103, name: "Keyboard", stock:
the order in which discounts and 20 }
taxes are applied, as this can ];
significantly impact the final price.
Common Pitfalls: // Simulate updating UI for products
[Link](product => {
Floating-Point Errors: Directly using
// In a real app, this would be
floating-point numbers for currency
triggered by data changes or page
calculations can lead to precision
load
issues. Consider using integers (e.g.,
[Link](`Product
cents) or a library for financial
${[Link]} (ID: ${[Link]}):
calculations.
${getStockStatus([Link])}`);
Hardcoding Values: Avoid // Example: Imagine these
hardcoding tax rates or discount elements exist in your HTML
percentages directly into functions. // updateProductUI([Link],
Pass them as parameters or retrieve [Link]);
them from a configuration. });
Practical Tips:
Dynamic Thresholds: Allow low
stock thresholds to be configurable
based on product type, supplier lead
times, or sales velocity.
Asynchronous Updates: In a live
system, stock levels often change.
Functions can be used to handle
asynchronous updates to inventory
and reflect these changes on the
front end in real-time.
Common Pitfalls:
Race Conditions: In multi-user
environments, two users might try to
purchase the last item
simultaneously. Functions interacting
with stock must handle these "race
conditions" to prevent overselling.
Inaccurate Data: Ensure your stock-
checking functions always query the
most up-to-date inventory data, not
cached or stale information.
Class Activity: Build a
Grading App
Time to put it all together! Create a program that demonstrates everything
we've learned through a practical application. This exercise will help
solidify your understanding of functions, conditionals, loops, and arrays.
01 02
Grade Calculator Function Average Calculator
Your first task is to write a function Next, you'll calculate the average of
that takes a numerical score as a set of test scores. For this, you'll
input and returns the corresponding need an array to store the scores
letter grade. This function will and a loop to iterate through them,
heavily rely on conditional summing up all values. Once the
statements (if, else if, else) to sum is calculated, divide it by the
evaluate the score against total number of scores to get the
predefined thresholds. average.
Requirements: Requirements:
A: 90 and above Use an array to store at least 5
B: 80-89 test scores (e.g., [85, 92, 78, 65,
95]).
C: 70-79
Use a loop (e.g., a for loop or
F: Below 70
forEach) to sum the scores.
Example Code Snippet:
Calculate the average and
display it.
function getLetterGrade(score)
Example Code Snippet:
{
// Check for invalid scores first
(e.g., negative or above 100) function
if (score < 0 || score > 100) { calculateAverage(scores) {
return "Invalid Score"; let sum = 0; // Initialize sum to
} zero
// Determine letter grade // Loop through each score in
based on score the array
if (score >= 90) { // If score is for (let i = 0; i < [Link];
90 or higher i++) {
return "A"; sum += scores[i]; // Add the
} else if (score >= 80) { // If current score to the sum
score is 80-89 }
return "B"; // Calculate average, ensuring
} else if (score >= 70) { // If no division by zero
score is 70-79 if ([Link] === 0) {
return "C"; return 0; // Return 0 if there
} else { // If score is below 70 are no scores
return "F"; }
} return sum / [Link]; //
} Divide sum by the number of
scores
Practical Tips & Pitfalls: }
Order Matters: Always check
const testScores = [85, 92, 78,
the highest scores first when
65, 95];
using multiple else if statements.
// [Link]("Average Score:",
If you checked for 'F' first, a calculateAverage(testScores));
score of 95 would incorrectly be
an 'F'.
Practical Tips & Pitfalls:
Edge Cases: Consider what
Empty Arrays: What if the array
happens with scores like 69.9 or
of scores is empty? Your
89.9. Integer vs. float handling
function should handle this
might be important depending
gracefully (e.g., return 0 or throw
on the language.
an error).
Input Validation: Add checks
Data Types: Ensure your sum
for scores outside the typical 0-
variable can hold potentially
100 range to make your function
large numbers without overflow,
more robust.
and that division results in
floating-point numbers for
accurate averages.
Looping Alternatives: Many
languages offer more modern
ways to loop (e.g., for...of,
forEach, or reduce method).
Explore these for cleaner code.
03
Complete Grading System
Now, integrate your two functions to create a full grading application. This
will involve defining scores for multiple students and then, for each
student, calculating their average score and assigning a letter grade. This
demonstrates how modular functions can be combined to build more
complex applications.
Requirements:
Define data for multiple students, each with an array of test scores.
For each student, calculate their average score using your
calculateAverage function.
Then, determine their letter grade using your getLetterGrade function.
Display the results clearly for each student.
Example Code Snippet:
// Assuming getLetterGrade and calculateAverage functions are
defined above
const students = [
{ name: "Alice", scores: [90, 88, 95, 92] },
{ name: "Bob", scores: [75, 68, 70, 72] },
{ name: "Charlie", scores: [55, 60, 48, 62] },
{ name: "Diana", scores: [100, 99, 100, 98] }
];
function processGrades(studentsData) {
[Link](student => {
const average = calculateAverage([Link]); // Calculate
average for student
const grade = getLetterGrade(average); // Get letter grade for that
average
[Link](`${[Link]}: Average Score =
${[Link](2)}, Grade = ${grade}`); // Display results
});
}
// Call the function to process all students
// processGrades(students);
Best Practices & Troubleshooting:
Modularity: By breaking the problem into smaller functions, your code
is easier to read, test, and debug.
Data Structure: Using objects (like { name: "Alice", scores: [...] }) is an
excellent way to organize related student data.
Testing: Test with various scenarios: students with high scores, low
scores, mixed scores, and even an invalid score if your getLetterGrade
function handles it.
Debugging: If output isn't as expected, use [Link]() statements
(or your language's equivalent) inside your functions to inspect
intermediate values like sum or average.
This activity provides a strong foundation for understanding how individual
programming concepts come together to form functional applications.