JavaScript Essentials

Loops: for, while, and do-while

45 min Lesson 9 of 60

Why Do We Need Loops?

Imagine you need to print the numbers 1 through 1000, or process every item in a shopping cart, or check every pixel in an image. Writing individual statements for each repetition would be impractical and unmaintainable. Loops are one of the most fundamental constructs in programming because they allow you to execute a block of code repeatedly based on a condition. Instead of writing the same code hundreds or thousands of times, you write it once and let the loop handle the repetition. Every serious JavaScript application relies heavily on loops for tasks like iterating over data, processing user input, rendering lists of elements, and handling asynchronous operations.

The for Loop

The for loop is the most commonly used loop in JavaScript. It is ideal when you know in advance how many times you want to repeat an action. The for loop packs three expressions into a single line: initialization, condition, and update. This compact syntax makes it easy to see the entire loop logic at a glance.

Anatomy of a for Loop

A for loop consists of three parts separated by semicolons inside parentheses, followed by a block of code in curly braces:

Example: The Three Parts of a for Loop

// Syntax:
// for (initialization; condition; update) {
//     code to execute each iteration
// }

for (let i = 0; i < 5; i++) {
    console.log('Iteration number: ' + i);
}

// Output:
// Iteration number: 0
// Iteration number: 1
// Iteration number: 2
// Iteration number: 3
// Iteration number: 4

Here is exactly what happens step by step: First, the initialization (let i = 0) runs once before the loop begins. It creates a counter variable. Second, the condition (i < 5) is checked before every iteration. If it evaluates to true, the loop body executes. If false, the loop ends immediately. Third, the update (i++) runs after each iteration, typically incrementing or decrementing the counter. Then the condition is checked again, and the cycle continues.

Example: Counting Backwards with a for Loop

for (let i = 10; i >= 1; i--) {
    console.log('Countdown: ' + i);
}
console.log('Liftoff!');

// Output:
// Countdown: 10
// Countdown: 9
// ... (continues)
// Countdown: 1
// Liftoff!

Example: Stepping by Two

// Print even numbers from 0 to 20
for (let i = 0; i <= 20; i += 2) {
    console.log(i);
}
// Output: 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20
Note: Always use let instead of var for your loop counter. With let, the variable is scoped to the loop block, which prevents unexpected behavior especially in asynchronous code and closures.

Looping Through Arrays

One of the most common uses of the for loop is iterating over arrays. You use the array's length property as the condition to ensure you visit every element without going out of bounds.

Example: Iterating Over an Array

let fruits = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'];

for (let i = 0; i < fruits.length; i++) {
    console.log('Fruit ' + (i + 1) + ': ' + fruits[i]);
}

// Output:
// Fruit 1: Apple
// Fruit 2: Banana
// Fruit 3: Cherry
// Fruit 4: Date
// Fruit 5: Elderberry

Example: Summing Array Values

let prices = [29.99, 9.99, 49.99, 14.99, 39.99];
let total = 0;

for (let i = 0; i < prices.length; i++) {
    total += prices[i];
}

console.log('Total: $' + total.toFixed(2));
// Output: Total: $144.95
Pro Tip: When looping through a large array in performance-critical code, consider caching the array length in a variable: for (let i = 0, len = arr.length; i < len; i++). In modern JavaScript engines this optimization is often done automatically, but it is still a good habit for clarity and shows your intent.

The while Loop

The while loop is used when you do not know in advance how many iterations are needed. It keeps running as long as a condition remains true. The condition is evaluated before each iteration, which means the loop body might never execute if the condition is false from the start.

Example: Basic while Loop

let count = 1;

while (count <= 5) {
    console.log('Count is: ' + count);
    count++;
}

// Output:
// Count is: 1
// Count is: 2
// Count is: 3
// Count is: 4
// Count is: 5

Example: User Input Simulation with while

// Simulating a scenario where we process data until a condition is met
let data = [3, 7, 2, 8, 1, 9, 4, 6];
let index = 0;
let sum = 0;

// Keep adding numbers until sum exceeds 15
while (sum <= 15 && index < data.length) {
    sum += data[index];
    console.log('Added ' + data[index] + ', running total: ' + sum);
    index++;
}

console.log('Final sum: ' + sum + ' after ' + index + ' additions');

Example: Finding an Element with while

let names = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve'];
let searchFor = 'Charlie';
let position = 0;

while (position < names.length && names[position] !== searchFor) {
    position++;
}

if (position < names.length) {
    console.log('Found ' + searchFor + ' at index ' + position);
} else {
    console.log(searchFor + ' was not found');
}
// Output: Found Charlie at index 2

The do-while Loop

The do-while loop is similar to the while loop, but with one critical difference: it always executes the loop body at least once because the condition is checked after each iteration, not before. This makes it perfect for situations where you need the code to run at least once regardless of the condition, such as displaying a menu or prompting for input.

Example: Basic do-while Loop

let number = 1;

do {
    console.log('Number: ' + number);
    number++;
} while (number <= 5);

// Output:
// Number: 1
// Number: 2
// Number: 3
// Number: 4
// Number: 5

Example: do-while Executes at Least Once

let value = 100;

// Even though the condition is false, the body runs once
do {
    console.log('This will print once! Value: ' + value);
    value++;
} while (value < 5);

// Output: This will print once! Value: 100
// Compare with while:
value = 100;
while (value < 5) {
    console.log('This will never print');
    value++;
}
// No output from the while loop

Example: Menu System with do-while

let menuOptions = ['View Profile', 'Edit Settings', 'Check Messages', 'Logout'];
let choice = 0;
let attempts = 0;

do {
    console.log('\n--- Main Menu ---');
    for (let i = 0; i < menuOptions.length; i++) {
        console.log((i + 1) + '. ' + menuOptions[i]);
    }
    // Simulating user choosing option 4 (Logout) on the third attempt
    attempts++;
    choice = attempts === 3 ? 4 : attempts;
    console.log('User selected option: ' + choice);
} while (choice !== 4);

console.log('Goodbye!');
Note: The semicolon after the while condition in a do-while loop is required. Forgetting it is a common syntax error. The syntax is: do { ... } while (condition);

break and continue Statements

The break statement immediately terminates the loop entirely, transferring control to the first statement after the loop. The continue statement skips the rest of the current iteration and moves to the next one. These statements give you fine-grained control over loop execution.

Example: Using break to Exit Early

let scores = [85, 92, 78, 95, 60, 45, 88, 73];

// Find the first failing score (below 50)
for (let i = 0; i < scores.length; i++) {
    if (scores[i] < 50) {
        console.log('First failing score: ' + scores[i] + ' at index ' + i);
        break; // Stop searching once we find the first failing score
    }
    console.log('Score ' + scores[i] + ' is passing');
}
// Output:
// Score 85 is passing
// Score 92 is passing
// Score 78 is passing
// Score 95 is passing
// Score 60 is passing
// First failing score: 45 at index 5

Example: Using continue to Skip Iterations

let numbers = [1, -2, 3, -4, 5, -6, 7, -8, 9, -10];
let positiveSum = 0;

for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] < 0) {
        continue; // Skip negative numbers
    }
    positiveSum += numbers[i];
    console.log('Added ' + numbers[i] + ', sum: ' + positiveSum);
}
console.log('Sum of positive numbers: ' + positiveSum);
// Output:
// Added 1, sum: 1
// Added 3, sum: 4
// Added 5, sum: 9
// Added 7, sum: 16
// Added 9, sum: 25
// Sum of positive numbers: 25
Warning: Be careful when using continue inside a while loop. If the update expression (like i++) comes after the continue statement, it will be skipped, potentially creating an infinite loop. Always make sure the loop counter is updated before the continue statement in while loops.

Nested Loops

You can place one loop inside another to handle multi-dimensional data structures or generate combinations. The inner loop completes all its iterations for each single iteration of the outer loop. Nested loops are common when working with grids, tables, matrices, or when comparing items in a list against each other.

Example: Multiplication Table with Nested Loops

// Generate a 5x5 multiplication table
for (let row = 1; row <= 5; row++) {
    let line = '';
    for (let col = 1; col <= 5; col++) {
        let product = row * col;
        line += product + '\t';
    }
    console.log(line);
}
// Output:
// 1    2    3    4    5
// 2    4    6    8    10
// 3    6    9    12   15
// 4    8    12   16   20
// 5    10   15   20   25

Example: Finding Pairs that Sum to a Target

let values = [2, 7, 11, 15, 3, 6];
let target = 9;

console.log('Pairs that sum to ' + target + ':');
for (let i = 0; i < values.length; i++) {
    for (let j = i + 1; j < values.length; j++) {
        if (values[i] + values[j] === target) {
            console.log(values[i] + ' + ' + values[j] + ' = ' + target);
        }
    }
}
// Output:
// Pairs that sum to 9:
// 2 + 7 = 9
// 3 + 6 = 9

Example: Processing a 2D Grid

let grid = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];

let gridTotal = 0;
for (let row = 0; row < grid.length; row++) {
    for (let col = 0; col < grid[row].length; col++) {
        gridTotal += grid[row][col];
        console.log('grid[' + row + '][' + col + '] = ' + grid[row][col]);
    }
}
console.log('Grid total: ' + gridTotal);
// Output: Grid total: 45

The for...in Loop

The for...in loop is designed specifically for iterating over the enumerable properties (keys) of an object. It gives you access to each property name, which you can then use to access the corresponding value. While it can technically be used on arrays, it is best reserved for objects because it iterates over all enumerable properties including inherited ones, which can lead to unexpected results with arrays.

Example: Iterating Over Object Properties

let student = {
    name: 'Alice',
    age: 22,
    major: 'Computer Science',
    gpa: 3.8,
    graduated: false
};

for (let key in student) {
    console.log(key + ': ' + student[key]);
}
// Output:
// name: Alice
// age: 22
// major: Computer Science
// gpa: 3.8
// graduated: false

Example: Using hasOwnProperty for Safety

let car = {
    make: 'Toyota',
    model: 'Camry',
    year: 2023,
    color: 'Silver'
};

for (let prop in car) {
    if (car.hasOwnProperty(prop)) {
        console.log(prop + ' = ' + car[prop]);
    }
}
// This ensures we only access the object's own properties,
// not inherited ones from the prototype chain
Warning: Avoid using for...in to iterate over arrays. It iterates over property names (which are string indices for arrays), not values. It also includes inherited enumerable properties and does not guarantee order. Use a standard for loop or for...of for arrays instead.

The for...of Loop

The for...of loop was introduced in ES6 and is designed for iterating over iterable objects such as arrays, strings, Maps, Sets, and NodeLists. Unlike for...in which gives you keys, for...of gives you values directly. It is the cleanest and most readable way to iterate over array elements and string characters.

Example: Iterating Over an Array with for...of

let colors = ['Red', 'Green', 'Blue', 'Yellow', 'Purple'];

for (let color of colors) {
    console.log('Color: ' + color);
}
// Output:
// Color: Red
// Color: Green
// Color: Blue
// Color: Yellow
// Color: Purple

Looping Through Strings

Strings are iterable in JavaScript, which means you can loop through them character by character using both the standard for loop and the for...of loop. This is useful for tasks like counting characters, reversing strings, checking for palindromes, or validating input.

Example: Counting Vowels in a String

let text = 'JavaScript is an amazing programming language';
let vowels = 'aeiouAEIOU';
let vowelCount = 0;

for (let char of text) {
    if (vowels.includes(char)) {
        vowelCount++;
    }
}
console.log('Vowel count: ' + vowelCount);
// Output: Vowel count: 16

Example: Reversing a String with a for Loop

let original = 'Hello World';
let reversed = '';

for (let i = original.length - 1; i >= 0; i--) {
    reversed += original[i];
}
console.log('Original: ' + original);
console.log('Reversed: ' + reversed);
// Output:
// Original: Hello World
// Reversed: dlroW olleH

Infinite Loop Dangers

An infinite loop occurs when the loop condition never becomes false. This causes the loop to run forever, freezing your browser tab or crashing your Node.js process. Infinite loops are one of the most common bugs for beginners and can happen with any type of loop. Understanding how they occur is essential to avoiding them.

Example: Common Infinite Loop Mistakes

// DANGER: Do NOT run these examples -- they will freeze your browser!

// Mistake 1: Forgetting to update the counter
// let i = 0;
// while (i < 10) {
//     console.log(i);
//     // Missing i++ -- i will always be 0!
// }

// Mistake 2: Condition that can never be false
// for (let i = 1; i > 0; i++) {
//     console.log(i);
//     // i keeps growing, always greater than 0
// }

// Mistake 3: Updating the wrong variable
// let x = 0;
// let y = 0;
// while (x < 10) {
//     y++;  // x never changes -- should be x++
// }

// Safe pattern: Always have a clear exit condition
let safeCounter = 0;
let maxIterations = 1000;
while (safeCounter < maxIterations) {
    // Do work here
    safeCounter++;
}
console.log('Loop completed safely after ' + safeCounter + ' iterations');
Warning: If you accidentally create an infinite loop in the browser, the tab will become unresponsive. In most browsers you can close the frozen tab or use the browser's task manager to kill it. Always double-check your loop conditions and update expressions before running your code. A good practice is to add a maximum iteration safeguard during development.

Loop Performance Considerations

Not all loops perform equally. When dealing with large datasets, the choice of loop and how you structure it can have a measurable impact on execution time. Here are some practical performance tips and patterns you should follow.

Example: Caching Length for Performance

let largeArray = new Array(1000000).fill(0).map((_, i) => i);

// Less efficient: length is accessed every iteration
// for (let i = 0; i < largeArray.length; i++) { ... }

// More efficient: length is cached
let len = largeArray.length;
for (let i = 0; i < len; i++) {
    // Process largeArray[i]
}

// Also efficient: cache in the for statement
for (let i = 0, n = largeArray.length; i < n; i++) {
    // Process largeArray[i]
}

Example: Avoiding Expensive Operations Inside Loops

let items = ['apple', 'banana', 'cherry', 'date', 'elderberry'];

// Bad: Creating a new regex every iteration
// for (let i = 0; i < items.length; i++) {
//     if (new RegExp('an').test(items[i])) { ... }
// }

// Good: Create the regex once outside the loop
let pattern = new RegExp('an');
for (let i = 0; i < items.length; i++) {
    if (pattern.test(items[i])) {
        console.log(items[i] + ' contains "an"');
    }
}
// Output:
// banana contains "an"

Labeled Statements

Labels allow you to name a loop and then use break or continue with that label to control which loop is affected. This is particularly useful with nested loops when you want to break out of an outer loop from inside an inner loop. Without labels, break and continue only affect the innermost loop.

Example: Breaking Out of Nested Loops with Labels

let matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];
let searchTarget = 5;
let found = false;

outerLoop: for (let row = 0; row < matrix.length; row++) {
    for (let col = 0; col < matrix[row].length; col++) {
        if (matrix[row][col] === searchTarget) {
            console.log('Found ' + searchTarget + ' at position [' + row + '][' + col + ']');
            found = true;
            break outerLoop; // Breaks out of BOTH loops
        }
    }
}
// Output: Found 5 at position [1][1]

Example: Using continue with Labels

outerLoop: for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
        if (j === 1) {
            continue outerLoop; // Skip to next iteration of outer loop
        }
        console.log('i = ' + i + ', j = ' + j);
    }
}
// Output:
// i = 0, j = 0
// i = 1, j = 0
// i = 2, j = 0
// Notice: j = 1 and j = 2 are never printed
Pro Tip: Labeled statements are rarely needed in day-to-day coding. If you find yourself needing labels frequently, consider refactoring your code into smaller functions. A function can use return to exit early, which is often cleaner than labeled breaks.

Real-World Example: Pagination

Pagination is a common pattern in web applications where you display data in pages rather than all at once. Loops are essential for calculating which items to display on the current page, generating page numbers, and slicing data arrays.

Example: Implementing Pagination Logic

let allProducts = [];
for (let i = 1; i <= 47; i++) {
    allProducts.push('Product ' + i);
}

let itemsPerPage = 10;
let currentPage = 3;
let totalPages = Math.ceil(allProducts.length / itemsPerPage);

// Calculate start and end indices for the current page
let startIndex = (currentPage - 1) * itemsPerPage;
let endIndex = Math.min(startIndex + itemsPerPage, allProducts.length);

console.log('Page ' + currentPage + ' of ' + totalPages);
console.log('Showing items ' + (startIndex + 1) + ' to ' + endIndex + ':');

for (let i = startIndex; i < endIndex; i++) {
    console.log('  ' + allProducts[i]);
}

// Generate page navigation
let pageNav = 'Pages: ';
for (let p = 1; p <= totalPages; p++) {
    if (p === currentPage) {
        pageNav += '[' + p + '] ';
    } else {
        pageNav += p + ' ';
    }
}
console.log(pageNav);
// Output:
// Page 3 of 5
// Showing items 21 to 30:
//   Product 21
//   Product 22
//   ... (and so on)
// Pages: 1 2 [3] 4 5

Real-World Example: Data Processing and Filtering

Loops are the backbone of data processing. In real applications, you frequently need to filter, transform, and aggregate data. Here is a comprehensive example that demonstrates multiple loop patterns working together to process a dataset of employee records.

Example: Processing Employee Data

let employees = [
    { name: 'Alice', department: 'Engineering', salary: 85000, active: true },
    { name: 'Bob', department: 'Marketing', salary: 62000, active: true },
    { name: 'Charlie', department: 'Engineering', salary: 92000, active: false },
    { name: 'Diana', department: 'Sales', salary: 71000, active: true },
    { name: 'Eve', department: 'Engineering', salary: 88000, active: true },
    { name: 'Frank', department: 'Marketing', salary: 58000, active: true },
    { name: 'Grace', department: 'Sales', salary: 67000, active: false },
    { name: 'Henry', department: 'Engineering', salary: 95000, active: true }
];

// Task 1: Find all active engineers
let activeEngineers = [];
for (let i = 0; i < employees.length; i++) {
    if (employees[i].department === 'Engineering' && employees[i].active) {
        activeEngineers.push(employees[i].name);
    }
}
console.log('Active Engineers: ' + activeEngineers.join(', '));
// Output: Active Engineers: Alice, Eve, Henry

// Task 2: Calculate average salary by department
let departments = {};
for (let i = 0; i < employees.length; i++) {
    let dept = employees[i].department;
    if (!departments[dept]) {
        departments[dept] = { total: 0, count: 0 };
    }
    departments[dept].total += employees[i].salary;
    departments[dept].count++;
}

for (let dept in departments) {
    let avg = (departments[dept].total / departments[dept].count).toFixed(2);
    console.log(dept + ' average salary: $' + avg);
}

// Task 3: Find the highest paid active employee
let highestPaid = null;
for (let i = 0; i < employees.length; i++) {
    if (employees[i].active) {
        if (highestPaid === null || employees[i].salary > highestPaid.salary) {
            highestPaid = employees[i];
        }
    }
}
console.log('Highest paid active employee: ' + highestPaid.name + ' ($' + highestPaid.salary + ')');
// Output: Highest paid active employee: Henry ($95000)

Real-World Example: Building a Simple Search

Search functionality is at the heart of most web applications. Here is how you might implement a basic search that scans through an array of products using loops.

Example: Product Search

let products = [
    { id: 1, name: 'Wireless Bluetooth Headphones', price: 59.99, category: 'Electronics' },
    { id: 2, name: 'Organic Green Tea', price: 12.99, category: 'Food' },
    { id: 3, name: 'Running Shoes', price: 89.99, category: 'Sports' },
    { id: 4, name: 'Bluetooth Speaker', price: 39.99, category: 'Electronics' },
    { id: 5, name: 'Yoga Mat', price: 24.99, category: 'Sports' },
    { id: 6, name: 'USB-C Charging Cable', price: 9.99, category: 'Electronics' }
];

let searchTerm = 'bluetooth';
let results = [];

for (let i = 0; i < products.length; i++) {
    let productName = products[i].name.toLowerCase();
    if (productName.indexOf(searchTerm.toLowerCase()) !== -1) {
        results.push(products[i]);
    }
}

console.log('Search results for "' + searchTerm + '": ' + results.length + ' found');
for (let i = 0; i < results.length; i++) {
    console.log('  - ' + results[i].name + ' ($' + results[i].price + ')');
}
// Output:
// Search results for "bluetooth": 2 found
//   - Wireless Bluetooth Headphones ($59.99)
//   - Bluetooth Speaker ($39.99)

Choosing the Right Loop

Each loop type serves a specific purpose. Choosing the right one makes your code clearer and less error-prone:

  • for -- Use when you know exactly how many iterations are needed, or when you need the index. Best for arrays when you need the index.
  • while -- Use when the number of iterations depends on a condition that changes during execution. Best for event-driven or condition-based repetition.
  • do-while -- Use when the loop body must execute at least once before the condition is checked. Best for menus, input validation, or retry logic.
  • for...in -- Use exclusively for iterating over object properties (keys). Never use for arrays.
  • for...of -- Use for iterating over values of iterables (arrays, strings, Maps, Sets). The cleanest syntax for array iteration when you do not need the index.

Practice Exercise

Complete these four challenges to solidify your understanding of JavaScript loops:

Challenge 1: FizzBuzz. Write a loop that prints numbers from 1 to 100. For multiples of 3, print "Fizz" instead of the number. For multiples of 5, print "Buzz". For multiples of both 3 and 5, print "FizzBuzz".

Challenge 2: Pattern Printer. Use nested loops to print this triangle pattern with asterisks: line 1 has 1 asterisk, line 2 has 2, and so on up to line 7.

Challenge 3: Array Deduplication. Given an array [1, 3, 5, 3, 7, 1, 9, 5, 7, 2], use a loop to create a new array containing only unique values. Do not use Set or any built-in methods besides push and includes.

Challenge 4: Object Statistics. Given an object representing daily temperatures like { Mon: 72, Tue: 68, Wed: 75, Thu: 80, Fri: 69, Sat: 77, Sun: 74 }, use a for...in loop to find and print the hottest day, the coldest day, and the average temperature for the week.

ES
Edrees Salih
16 hours ago

We are still cooking the magic in the way!