We are still cooking the magic in the way!
Loops: for, while, and do-while
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
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
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!');
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
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
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');
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
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.