Loops & Iterations in JavaScript
Loops are essential programming constructs that allow you to execute a block of code repeatedly. JavaScript offers several types of loops, each with its own use cases and syntax. Understanding loops is crucial for efficient programming and data manipulation.
Introduction to Loops
Loops are used to execute the same block of code multiple times until a specific condition is met. They help automate repetitive tasks and process collections of data efficiently. JavaScript provides several types of loops to handle different scenarios.
Before diving into specific loop types, let's understand some common loop components:
- Initialization: Sets up a variable before the loop begins (usually a counter)
- Condition: Evaluated before each loop iteration; the loop continues as long as it's true
- Iteration statement: Updates the loop variable, typically incrementing or decrementing a counter
- Loop body: The code block that runs on each iteration
The for Loop
The for
loop is one of the most commonly used loops in JavaScript. It's ideal when you know exactly how many times you want to execute a block of code.
// Basic for loop syntax
for (initialization; condition; iteration) {
// code to be executed
}
// Example: Counting from 1 to 5
for (let i = 1; i <= 5; i++) {
console.log(i); // Outputs: 1, 2, 3, 4, 5
}
// Example: Counting backwards from 5 to 1
for (let i = 5; i >= 1; i--) {
console.log(i); // Outputs: 5, 4, 3, 2, 1
}
// Example: Skipping numbers (counting by 2)
for (let i = 1; i <= 10; i += 2) {
console.log(i); // Outputs: 1, 3, 5, 7, 9
}
// Example: Multiple initialization and iteration expressions
for (let i = 0, j = 10; i < j; i++, j--) {
console.log(i, j); // Outputs: 0 10, 1 9, 2 8, 3 7, 4 6
}
Nested for Loops
You can place one loop inside another, creating nested loops. This is particularly useful for working with multi-dimensional arrays or generating combinations.
// Example: Creating a multiplication table
for (let i = 1; i <= 5; i++) {
for (let j = 1; j <= 5; j++) {
console.log(`${i} × ${j} = ${i * j}`);
}
console.log('-------------'); // Separator between tables
}
// Example: Working with a 2D array
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
console.log(`Element at position [${i}][${j}]: ${matrix[i][j]}`);
}
}
The while Loop
The while
loop executes a block of code as long as a specified condition is true. It's useful when you don't know in advance how many times the loop should run.
// Basic while loop syntax
while (condition) {
// code to be executed
}
// Example: Counting from 1 to 5
let i = 1;
while (i <= 5) {
console.log(i); // Outputs: 1, 2, 3, 4, 5
i++;
}
// Example: Random number generation until a condition is met
let randomNum = 0;
let attempts = 0;
while (randomNum < 0.8) {
randomNum = Math.random(); // Generates a random number between 0 and 1
attempts++;
console.log(`Attempt ${attempts}: ${randomNum}`);
}
console.log(`It took ${attempts} attempts to generate a number >= 0.8`);
while
loops! If the condition never becomes false, the loop will run indefinitely, creating an infinite loop that can crash your browser or application.
The do...while Loop
The do...while
loop is similar to the while
loop, but with one key difference: it executes the code block once before checking the condition, ensuring that the code runs at least once regardless of the condition.
// Basic do...while loop syntax
do {
// code to be executed
} while (condition);
// Example: Counting from 1 to 5
let i = 1;
do {
console.log(i); // Outputs: 1, 2, 3, 4, 5
i++;
} while (i <= 5);
// Example: Code runs at least once even if condition is initially false
let j = 10;
do {
console.log(j); // Outputs: 10 (runs once even though j > 5)
j++;
} while (j <= 5);
// Example: Menu-driven program
let choice;
do {
choice = prompt('Select an option (1-3) or 0 to exit:');
switch(choice) {
case '1':
console.log('You selected option 1');
break;
case '2':
console.log('You selected option 2');
break;
case '3':
console.log('You selected option 3');
break;
case '0':
console.log('Exiting program...');
break;
default:
console.log('Invalid option. Try again.');
}
} while (choice !== '0');
The for...in Loop
The for...in
loop iterates over all enumerable properties of an object. It's primarily designed for objects but can also be used with arrays (though not recommended for arrays).
// Basic for...in loop syntax
for (variable in object) {
// code to be executed
}
// Example: Iterating over object properties
const person = {
firstName: 'John',
lastName: 'Doe',
age: 30,
occupation: 'Developer'
};
for (let key in person) {
console.log(`${key}: ${person[key]}`);
// Outputs:
// firstName: John
// lastName: Doe
// age: 30
// occupation: Developer
}
// Example: Using for...in with arrays (not recommended)
const colors = ['red', 'green', 'blue'];
for (let index in colors) {
console.log(`Index ${index}: ${colors[index]}`);
// Outputs:
// Index 0: red
// Index 1: green
// Index 2: blue
}
// Example: Why for...in is not ideal for arrays
Array.prototype.customProperty = 'This is a custom property';
const numbers = [1, 2, 3];
for (let i in numbers) {
console.log(i); // Outputs: '0', '1', '2', 'customProperty'
}
for...in
loop should generally not be used for arrays, especially if the order of iteration is important. It iterates over all enumerable properties, including those in the prototype chain, which may lead to unexpected results.
The for...of Loop
Introduced in ES6 (ECMAScript 2015), the for...of
loop iterates over iterable objects such as arrays, strings, maps, sets, and more. It provides a cleaner and more reliable way to iterate over collections compared to for...in
.
// Basic for...of loop syntax
for (variable of iterable) {
// code to be executed
}
// Example: Iterating over an array
const fruits = ['apple', 'banana', 'orange', 'mango'];
for (let fruit of fruits) {
console.log(fruit);
// Outputs: apple, banana, orange, mango
}
// Example: Iterating over a string
const message = 'Hello';
for (let char of message) {
console.log(char);
// Outputs: H, e, l, l, o
}
// Example: Iterating over a Map
const userRoles = new Map();
userRoles.set('John', 'Admin');
userRoles.set('Jane', 'Editor');
userRoles.set('Bob', 'Subscriber');
for (let [user, role] of userRoles) {
console.log(`${user} is a ${role}`);
// Outputs:
// John is a Admin
// Jane is a Editor
// Bob is a Subscriber
}
// Example: Iterating over a Set
const uniqueNumbers = new Set([1, 2, 3, 3, 4, 4, 5]);
for (let num of uniqueNumbers) {
console.log(num);
// Outputs: 1, 2, 3, 4, 5 (duplicates removed)
}
Array Iteration Methods
Modern JavaScript provides several built-in array methods that allow you to iterate over arrays without using traditional loops. These methods are more declarative and often lead to cleaner, more readable code.
forEach()
The forEach()
method executes a provided function once for each array element.
// Basic forEach syntax
array.forEach(function(currentValue, index, array) {
// code to be executed for each element
});
// Example: Simple iteration
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(function(number) {
console.log(number * 2);
// Outputs: 2, 4, 6, 8, 10
});
// Example: Using all parameters
const fruits = ['apple', 'banana', 'orange'];
fruits.forEach(function(fruit, index, array) {
console.log(`${index}: ${fruit} (from array of length ${array.length})`);
// Outputs:
// 0: apple (from array of length 3)
// 1: banana (from array of length 3)
// 2: orange (from array of length 3)
});
// Example: Using arrow function syntax
const prices = [19.99, 9.99, 29.99, 14.99];
prices.forEach((price, index) => {
console.log(`Item ${index + 1}: $${price}`);
});
map()
The map()
method creates a new array with the results of calling a provided function on every element in the calling array.
// Basic map syntax
const newArray = array.map(function(currentValue, index, array) {
// return element for newArray
});
// Example: Doubling numbers
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(function(number) {
return number * 2;
});
console.log(doubled); // Outputs: [2, 4, 6, 8, 10]
// Example: Converting object array
const users = [
{ id: 1, name: 'John', age: 30 },
{ id: 2, name: 'Jane', age: 25 },
{ id: 3, name: 'Bob', age: 40 }
];
const usernames = users.map(user => user.name);
console.log(usernames); // Outputs: ['John', 'Jane', 'Bob']
// Example: Creating HTML elements
const fruits = ['apple', 'banana', 'orange'];
const listItems = fruits.map(fruit => `${fruit} `);
const html = `${listItems.join('')}
`;
console.log(html);
// Outputs: - apple
- banana
- orange
filter()
The filter()
method creates a new array with all elements that pass the test implemented by the provided function.
// Basic filter syntax
const newArray = array.filter(function(currentValue, index, array) {
// return true to keep element, false to exclude it
});
// Example: Filtering even numbers
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evenNumbers = numbers.filter(function(number) {
return number % 2 === 0;
});
console.log(evenNumbers); // Outputs: [2, 4, 6, 8, 10]
// Example: Filtering objects
const users = [
{ id: 1, name: 'John', age: 30, active: true },
{ id: 2, name: 'Jane', age: 25, active: false },
{ id: 3, name: 'Bob', age: 40, active: true },
{ id: 4, name: 'Alice', age: 22, active: true }
];
const activeUsers = users.filter(user => user.active);
console.log(activeUsers);
// Outputs: [{ id: 1, name: 'John', age: 30, active: true }, { id: 3, name: 'Bob', age: 40, active: true }, { id: 4, name: 'Alice', age: 22, active: true }]
// Example: Filtering strings
const words = ['apple', 'banana', 'orange', 'kiwi', 'pineapple', 'grape'];
const longWords = words.filter(word => word.length > 5);
console.log(longWords); // Outputs: ['banana', 'orange', 'pineapple']
reduce()
The reduce()
method executes a reducer function on each element of the array, resulting in a single output value. It's incredibly versatile and can be used for summing numbers, flattening arrays, grouping objects, and more.
// Basic reduce syntax
const result = array.reduce(function(accumulator, currentValue, index, array) {
// return updated accumulator
}, initialValue);
// Example: Summing numbers
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce(function(total, number) {
return total + number;
}, 0);
console.log(sum); // Outputs: 15
// Example: Flattening an array of arrays
const arrays = [[1, 2], [3, 4], [5, 6]];
const flattened = arrays.reduce(function(result, array) {
return result.concat(array);
}, []);
console.log(flattened); // Outputs: [1, 2, 3, 4, 5, 6]
// Example: Counting occurrences
const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
const fruitCount = fruits.reduce(function(count, fruit) {
count[fruit] = (count[fruit] || 0) + 1;
return count;
}, {});
console.log(fruitCount);
// Outputs: { apple: 3, banana: 2, orange: 1 }
// Example: Grouping objects by property
const people = [
{ name: 'John', age: 30, department: 'Engineering' },
{ name: 'Jane', age: 25, department: 'Marketing' },
{ name: 'Bob', age: 40, department: 'Engineering' },
{ name: 'Alice', age: 22, department: 'Marketing' }
];
const byDepartment = people.reduce(function(groups, person) {
const department = person.department;
groups[department] = groups[department] || [];
groups[department].push(person);
return groups;
}, {});
console.log(byDepartment);
// Outputs: {
// Engineering: [
// { name: 'John', age: 30, department: 'Engineering' },
// { name: 'Bob', age: 40, department: 'Engineering' }
// ],
// Marketing: [
// { name: 'Jane', age: 25, department: 'Marketing' },
// { name: 'Alice', age: 22, department: 'Marketing' }
// ]
// }
find() and findIndex()
The find()
method returns the first element in the array that satisfies the provided testing function. The findIndex()
method returns the index of the first element that satisfies the testing function.
// Basic find and findIndex syntax
const foundElement = array.find(function(currentValue, index, array) {
// return true for the element you want to find
});
const foundIndex = array.findIndex(function(currentValue, index, array) {
// return true for the element whose index you want to find
});
// Example: Finding an object in an array
const users = [
{ id: 1, name: 'John', age: 30 },
{ id: 2, name: 'Jane', age: 25 },
{ id: 3, name: 'Bob', age: 40 }
];
const user = users.find(user => user.id === 2);
console.log(user); // Outputs: { id: 2, name: 'Jane', age: 25 }
// Example: Finding the index of an element
const fruits = ['apple', 'banana', 'orange', 'mango'];
const index = fruits.findIndex(fruit => fruit === 'orange');
console.log(index); // Outputs: 2
some() and every()
The some()
method tests whether at least one element in the array passes the test implemented by the provided function. The every()
method tests whether all elements in the array pass the test.
// Basic some and every syntax
const hasMatch = array.some(function(currentValue, index, array) {
// return true if element meets condition
});
const allMatch = array.every(function(currentValue, index, array) {
// return true if element meets condition
});
// Example: Checking if any number is even
const numbers = [1, 3, 5, 7, 8, 9];
const hasEven = numbers.some(number => number % 2 === 0);
console.log(hasEven); // Outputs: true (because 8 is even)
// Example: Checking if all users are adults
const users = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
{ name: 'Bob', age: 17 }
];
const allAdults = users.every(user => user.age >= 18);
console.log(allAdults); // Outputs: false (because Bob is 17)
Breaking and Continuing Loops
JavaScript provides statements to control the flow of loops: break
and continue
.
The break Statement
The break
statement terminates the current loop, switch, or label statement and transfers program control to the statement following the terminated statement.
// Example: Breaking out of a loop
for (let i = 1; i <= 10; i++) {
if (i === 5) {
break; // Exit the loop when i is 5
}
console.log(i); // Outputs: 1, 2, 3, 4
}
// Example: Finding an element in an array
const numbers = [1, 3, 5, 7, 9, 11, 13];
let foundIndex = -1;
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] > 10) {
foundIndex = i;
break; // Exit the loop once we find what we're looking for
}
}
console.log(`First number greater than 10 found at index ${foundIndex}`); // Outputs: First number greater than 10 found at index 5
The continue Statement
The continue
statement skips the current iteration of a loop and continues with the next iteration.
// Example: Skipping even numbers
for (let i = 1; i <= 10; i++) {
if (i % 2 === 0) {
continue; // Skip even numbers
}
console.log(i); // Outputs: 1, 3, 5, 7, 9
}
// Example: Processing an array with conditions
const scores = [85, 40, 92, 55, 30, 98, 72];
let passCount = 0;
for (let i = 0; i < scores.length; i++) {
if (scores[i] < 60) {
continue; // Skip failing scores
}
passCount++;
console.log(`Passing score: ${scores[i]}`);
}
console.log(`${passCount} students passed the exam.`);
Labeled Statements
JavaScript allows you to label statements, which can be useful with break
and continue
in nested loops.
// Example: Breaking out of nested loops
outerLoop: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) {
break outerLoop; // Breaks out of both loops
}
console.log(`i = ${i}, j = ${j}`);
}
}
// Outputs:
// i = 0, j = 0
// i = 0, j = 1
// i = 0, j = 2
// i = 1, j = 0
// Example: Continuing outer loop
outerLoop: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (j === 1) {
continue outerLoop; // Skips to the next iteration of the outer loop
}
console.log(`i = ${i}, j = ${j}`);
}
}
// Outputs:
// i = 0, j = 0
// i = 1, j = 0
// i = 2, j = 0
Infinite Loops
An infinite loop occurs when a loop's condition never evaluates to false. While usually unintentional, there are cases where you might want to create an intentional infinite loop with a manual exit condition.
// Example: Intentional infinite loop with a break condition
let counter = 0;
// This is an infinite loop that will run until the break condition is met
while (true) {
counter++;
console.log(`Iteration: ${counter}`);
if (counter >= 5) {
console.log('Breaking out of the loop');
break; // Exit condition
}
}
// Example: Common infinite loop mistakes
// 1. Forgetting to increment the counter
// let i = 0;
// while (i < 10) {
// console.log(i); // This will print 0 forever because i never changes
// // Missing i++
// }
// 2. Condition that's always true
// for (let i = 10; i > 0; i++) { // i will always be greater than 0
// console.log(i);
// }
// 3. Using assignment instead of comparison
// let x = 5;
// while (x = 5) { // This assigns 5 to x, which is truthy
// console.log(x);
// }
Performance Considerations
When working with loops, especially for large data sets, performance becomes an important consideration. Here are some tips for optimizing loop performance:
// Example: Caching array length
const arr = [1, 2, 3, 4, 5, /* ... thousands of items ... */];
// Less efficient - length is recalculated in each iteration
for (let i = 0; i < arr.length; i++) {
// Process arr[i]
}
// More efficient - length is calculated once
for (let i = 0, len = arr.length; i < len; i++) {
// Process arr[i]
}
// Example: Reverse loop for faster array operations (in some cases)
for (let i = arr.length - 1; i >= 0; i--) {
// Process arr[i]
}
// Example: Avoiding unnecessary work inside loops
const items = [/* ... large array ... */];
// Less efficient - creates a new Date object in each iteration
for (let i = 0; i < items.length; i++) {
items[i].timestamp = new Date().toISOString();
}
// More efficient - creates the Date object once
const timestamp = new Date().toISOString();
for (let i = 0; i < items.length; i++) {
items[i].timestamp = timestamp;
}
Advanced Iteration Techniques
Modern JavaScript offers several advanced techniques for iteration beyond traditional loops.
Generators and Iterators
Generators are functions that can be paused and resumed, allowing for on-demand value generation. They're defined using the function*
syntax and use yield
to provide values.
// Example: Simple generator function
function* countUp(max) {
let count = 1;
while (count <= max) {
yield count++;
}
}
// Using the generator
const counter = countUp(5);
console.log(counter.next().value); // 1
console.log(counter.next().value); // 2
console.log(counter.next().value); // 3
console.log(counter.next().value); // 4
console.log(counter.next().value); // 5
console.log(counter.next().value); // undefined
// Example: Iterating over a generator with for...of
function* fibonacci(n) {
let [a, b] = [0, 1];
for (let i = 0; i < n; i++) {
yield a;
[a, b] = [b, a + b];
}
}
for (const num of fibonacci(10)) {
console.log(num);
// Outputs: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
}
// Example: Creating a custom iterable object
const customIterable = {
*[Symbol.iterator]() {
yield 'This';
yield 'is';
yield 'iterable';
}
};
for (const word of customIterable) {
console.log(word);
// Outputs: This, is, iterable
}
Recursive Iteration
Recursion is a technique where a function calls itself to solve a problem. It can be an alternative to loops for certain types of problems, especially those involving tree-like data structures.
// Example: Recursive function to calculate factorial
function factorial(n) {
// Base case
if (n <= 1) {
return 1;
}
// Recursive case
return n * factorial(n - 1);
}
console.log(factorial(5)); // Outputs: 120 (5 * 4 * 3 * 2 * 1)
// Example: Traversing a nested object structure
const nestedObject = {
name: 'Level 1',
children: [
{
name: 'Level 2-1',
children: [
{ name: 'Level 3-1', value: 100 },
{ name: 'Level 3-2', value: 200 }
]
},
{
name: 'Level 2-2',
children: [
{ name: 'Level 3-3', value: 300 }
]
}
]
};
function sumValues(obj) {
let sum = 0;
// Add the current object's value if it exists
if (obj.value !== undefined) {
sum += obj.value;
}
// Recursively process children if they exist
if (obj.children && obj.children.length > 0) {
for (const child of obj.children) {
sum += sumValues(child);
}
}
return sum;
}
console.log(sumValues(nestedObject)); // Outputs: 600 (100 + 200 + 300)
Asynchronous Iteration
ES2018 introduced asynchronous iteration, allowing you to iterate over data that is fetched asynchronously.
// Example: Asynchronous generator function
async function* fetchUserData(userIds) {
for (const id of userIds) {
// Simulate API call
const response = await fetch(`https://api.example.com/users/${id}`);
const userData = await response.json();
yield userData;
}
}
// Using async iteration
async function processUsers() {
const userIds = [1, 2, 3, 4, 5];
for await (const user of fetchUserData(userIds)) {
console.log(`Processing user: ${user.name}`);
// Process each user as data becomes available
}
}
// Example: Creating a custom async iterable
const asyncIterable = {
async *[Symbol.asyncIterator]() {
yield await Promise.resolve('Hello');
yield await Promise.resolve('Async');
yield await Promise.resolve('World');
}
};
(async () => {
for await (const word of asyncIterable) {
console.log(word);
// Outputs: Hello, Async, World
}
})();
Common Loop Patterns
Here are some common patterns and techniques used with loops in JavaScript:
Pagination
// Example: Implementing pagination
async function fetchAllPages() {
let currentPage = 1;
let hasMorePages = true;
const allResults = [];
while (hasMorePages) {
console.log(`Fetching page ${currentPage}...`);
// Simulate API call with pagination
const response = await fetch(`https://api.example.com/data?page=${currentPage}&limit=100`);
const data = await response.json();
// Add results to our collection
allResults.push(...data.results);
// Check if there are more pages
hasMorePages = data.hasNextPage;
currentPage++;
// Optional: Add a delay to avoid rate limiting
await new Promise(resolve => setTimeout(resolve, 300));
}
console.log(`Fetched ${allResults.length} total items from ${currentPage - 1} pages`);
return allResults;
}
Batch Processing
// Example: Processing data in batches
function processBatches(items, batchSize = 100) {
const totalItems = items.length;
const totalBatches = Math.ceil(totalItems / batchSize);
console.log(`Processing ${totalItems} items in ${totalBatches} batches...`);
for (let i = 0; i < totalBatches; i++) {
const start = i * batchSize;
const end = Math.min(start + batchSize, totalItems);
const batch = items.slice(start, end);
console.log(`Processing batch ${i + 1}/${totalBatches} (items ${start + 1}-${end})`);
// Process the current batch
processBatch(batch);
}
console.log('All batches processed successfully!');
}
function processBatch(batch) {
// Process each item in the batch
for (const item of batch) {
// Process individual item
console.log(`Processing item: ${item}`);
}
}
Polling
// Example: Polling an API until a condition is met
async function pollUntilComplete(jobId, maxAttempts = 30, interval = 2000) {
let attempts = 0;
while (attempts < maxAttempts) {
attempts++;
console.log(`Polling attempt ${attempts}/${maxAttempts}...`);
// Simulate checking job status
const response = await fetch(`https://api.example.com/jobs/${jobId}`);
const job = await response.json();
if (job.status === 'completed') {
console.log('Job completed successfully!');
return job.result;
}
if (job.status === 'failed') {
throw new Error(`Job failed: ${job.error}`);
}
console.log(`Job status: ${job.status}, progress: ${job.progress}%`);
// Wait before next attempt
await new Promise(resolve => setTimeout(resolve, interval));
}
throw new Error(`Polling timed out after ${maxAttempts} attempts`);
}
Best Practices for Loops
Follow these best practices to write clean, efficient, and maintainable loop code:
- Choose the right loop type for your specific use case
- Avoid modifying the loop variable within the loop body (except in the iteration statement)
- Keep loop bodies small and focused - consider extracting complex logic into separate functions
- Be careful with asynchronous operations in loops - consider using
Promise.all()
orfor await...of
- Optimize performance by minimizing work inside loops and caching values when possible
- Use meaningful variable names for loop counters when they represent something specific
- Consider functional alternatives like
map()
,filter()
, andreduce()
for cleaner, more declarative code - Add comments to explain complex loop logic or non-obvious termination conditions
Common Pitfalls and How to Avoid Them
Here are some common mistakes when working with loops and how to avoid them:
// Pitfall 1: Infinite loops
// Avoid by ensuring your loop condition will eventually become false
// Pitfall 2: Off-by-one errors
// Example: Accessing array elements
const arr = ['a', 'b', 'c'];
// Incorrect - will cause an error
// for (let i = 1; i <= arr.length; i++) {
// console.log(arr[i]); // Starts at index 1, goes to index 3 (doesn't exist)
// }
// Correct
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]); // Starts at index 0, goes to index 2
}
// Pitfall 3: Forgetting to initialize variables
// Always initialize variables before using them in loops
// Pitfall 4: Closure issues in loops
// Example: Creating functions inside loops
// Problematic (pre-ES6)
const funcs = [];
for (var i = 0; i < 3; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs[0](); // Outputs: 3 (not 0 as expected)
funcs[1](); // Outputs: 3
funcs[2](); // Outputs: 3
// Solution 1: Use let instead of var (ES6+)
const funcs2 = [];
for (let j = 0; j < 3; j++) {
funcs2.push(function() {
console.log(j);
});
}
funcs2[0](); // Outputs: 0
funcs2[1](); // Outputs: 1
funcs2[2](); // Outputs: 2
// Solution 2: Use an IIFE (pre-ES6)
const funcs3 = [];
for (var k = 0; k < 3; k++) {
funcs3.push((function(value) {
return function() {
console.log(value);
};
})(k));
}
funcs3[0](); // Outputs: 0
funcs3[1](); // Outputs: 1
funcs3[2](); // Outputs: 2
Practice Exercise
Let's put your knowledge of loops to the test with a practical exercise. Try to solve the following problem:
Challenge: FizzBuzz with Loops
Write a program that prints numbers from 1 to 100. But for multiples of 3, print "Fizz" instead of the number, and for multiples of 5, print "Buzz". For numbers that are multiples of both 3 and 5, print "FizzBuzz".
Implement this using at least two different types of loops.
Hint!
You'll need to use the modulo operator (%) to check if a number is divisible by 3 or 5. Remember to check for numbers divisible by both 3 and 5 first!
// Solution using a for loop
function fizzBuzzWithForLoop() {
for (let i = 1; i <= 100; i++) {
if (i % 3 === 0 && i % 5 === 0) {
console.log('FizzBuzz');
} else if (i % 3 === 0) {
console.log('Fizz');
} else if (i % 5 === 0) {
console.log('Buzz');
} else {
console.log(i);
}
}
}
// Solution using a while loop
function fizzBuzzWithWhileLoop() {
let i = 1;
while (i <= 100) {
let output = '';
if (i % 3 === 0) output += 'Fizz';
if (i % 5 === 0) output += 'Buzz';
console.log(output || i);
i++;
}
}
// Solution using array methods
function fizzBuzzWithArrayMethods() {
Array.from({ length: 100 }, (_, i) => i + 1).forEach(num => {
let output = '';
if (num % 3 === 0) output += 'Fizz';
if (num % 5 === 0) output += 'Buzz';
console.log(output || num);
});
}
Summary
Loops and iterations are fundamental concepts in JavaScript programming. They allow you to execute code repeatedly, process collections of data, and implement complex algorithms efficiently. In this tutorial, we've covered:
- Different types of loops:
for
,while
,do...while
,for...in
, andfor...of
- Array iteration methods:
forEach()
,map()
,filter()
,reduce()
, and more - Flow control with
break
andcontinue
- Advanced techniques like generators, recursion, and asynchronous iteration
- Common patterns, best practices, and pitfalls to avoid
Understanding these concepts will help you write more efficient, readable, and maintainable JavaScript code. As you continue your JavaScript journey, you'll find yourself using these techniques regularly to solve a wide variety of programming challenges.