Best Practices for Iteration in JavaScript

Best Practices for Iteration in JavaScript

This article explains the best practices for iteration in JavaScript.

YouTube Video

Best Practices for Iteration in JavaScript

In JavaScript, it is common to use for loops for iteration. Here, we will provide a detailed explanation of the best practices for using for loops to write efficient and readable code.

Choose the Right Looping Structure

JavaScript provides multiple looping constructs, each suited for different purposes.

 1// Example of a for loop
 2for (let i = 0; i < 5; i++) {
 3    console.log(i);
 4}
 5
 6// Example of a for...of loop
 7const array = [10, 20, 30];
 8for (const value of array) {
 9    console.log(value);
10}
11
12// Example of a for...in loop
13const obj = { a: 1, b: 2, c: 3 };
14for (const key in obj) {
15    console.log(`${key}: ${obj[key]}`);
16}
17
18// Example of a while loop
19let count = 0;
20while (count < 5) {
21    console.log(count);
22    count++;
23}
  • for statement is suitable when the number of iterations is predetermined.
  • for...of statement is suitable for concisely processing arrays and iterable objects.
  • for...in statement is used to iterate over an object's properties. However, it is not suitable for arrays.
  • while statement and do...while statement are used to control loops based on conditions.

Utilizing the forEach method and for...of statement

When looping through an array, using a for statement to access the index is common, but the forEach method or for...of statement may be more readable.

 1// Using a standard for loop
 2const array = ["apple", "banana", "cherry"];
 3for (let i = 0; i < array.length; i++) {
 4    console.log(array[i]);
 5}
 6
 7// Using forEach
 8array.forEach(item => console.log(item));
 9
10// Using for...of
11for (const item of array) {
12    console.log(item);
13}
  • for statement allows explicit index management while iterating.
  • forEach method uses a callback function to process each element concisely.
  • for...of statement is highly readable and allows direct access to each element in an array.

Optimize Loop Conditions

Since the loop condition is evaluated repeatedly, avoiding unnecessary computations can improve performance.

 1const names = ["Alice", "Bob", "Charlie"];
 2const scores = [85, 92, 78];
 3
 4// Inefficient example
 5for (let i = 0; i < Math.min(names.length, scores.length); i++) {
 6    console.log(`${names[i]} scored ${scores[i]}`);
 7}
 8
 9// Efficient example
10for (let i = 0, len = Math.min(names.length, scores.length); i < len; i++) {
11    console.log(`${names[i]} scored ${scores[i]}`);
12}
  • As shown in this example, storing the calculation result in a variable beforehand allows for more efficient loop execution.
 1const scores = [85, 92, 78];
 2let sum = 0;
 3let sum2 = 0;
 4
 5// Inefficient example
 6for (let i = 0; i < scores.length; i++) {
 7    sum += scores[i];
 8}
 9console.log(`Total score : ${sum}`);
10
11// Efficient example
12for (let i = scores.length - 1; i >= 0; i--) {
13    sum2 += scores[i];
14}
15console.log(`Total score : ${sum2}`);
  • As shown in this example, reversing the condition can sometimes be more efficient.

Optimizing Loop Processing

Since loop processing is executed repeatedly, avoiding unnecessary calculations can improve performance.

 1const array = ["apple", "banana", "cherry"];
 2
 3// Inefficient example
 4for (let i = 0; i < 100; i++) {
 5    const element = document.querySelector("#myElement");
 6    element.textContent = `Count: ${i}`;
 7}
 8
 9// Efficient example
10const element = document.querySelector("#myElement");
11for (let i = 0; i < 100; i++) {
12    element.textContent = `Count: ${i}`;
13}
  • In this example, by moving the querySelector method outside the loop, unnecessary repeated calculations are eliminated.

Be Aware of Scope

Use let or const to ensure variables inside the loop have appropriate scope. Since var is limited to function scope, it may cause unexpected behavior.

 1// Using let
 2for (let i = 0; i < 3; i++) {
 3    console.log(i);
 4}
 5
 6// Potential issue with var
 7for (var i = 0; i < 3; i++) {
 8    setTimeout(() => console.log(i), 1000); // 3, 3, 3
 9}
10
11// Using let to avoid the issue
12for (let i = 0; i < 3; i++) {
13    setTimeout(() => console.log(i), 1000); // 0, 1, 2
14}
  • var has function scope, so after the loop i is 3, and all functions executed by setTimeout output 3.
  • Using let, the i inside the setTimeout callback function refers to a new value for each loop, so 0, 1, 2 are output as expected.

Improve Readability with Early Exits

To simplify loop processing, use break and continue appropriately to enhance readability.

 1// Example using break
 2for (let i = 0; i < 10; i++) {
 3    if (i === 5) {
 4        break; // Exit the loop
 5    }
 6    console.log(i);
 7}
 8
 9// Example using continue
10for (let i = 0; i < 10; i++) {
11    if (i % 2 === 0) {
12        continue; // Skip to the next iteration
13    }
14    console.log(i);
15}
  • Using break allows you to terminate loop processing midway, skipping all subsequent iterations.
  • Using continue allows you to skip the current loop process and move to the next iteration.

Avoid Deep Nesting

Deep nesting makes code harder to read, so aim to keep nesting shallow by using early returns or splitting functionality into functions.

 1// Deeply nested example
 2for (let i = 0; i < 5; i++) {
 3    for (let j = 0; j < 5; j++) {
 4        if (i + j > 5) {
 5            console.log(i, j);
 6        }
 7    }
 8}
 9
10// Improved using function decomposition
11function processPairs(i) {
12    for (let j = 0; j < 5; j++) {
13        if (i + j > 5) {
14            console.log(i, j);
15        }
16    }
17}
18
19for (let i = 0; i < 5; i++) {
20    processPairs(i);
21}
  • In this example, functions are used to reduce nesting.

Consider error handling

If there is a possibility of errors occurring within the loop, implement proper error handling.

 1const data = ["123", "abc", "456", "xyz"];
 2
 3// Without Error Handling
 4for (const item of data) {
 5    const result = parseInt(item);
 6    console.log(`Parsed value: ${result}`);
 7}
 8
 9// With Error Handling
10for (const item of data) {
11    try {
12        const result = parseInt(item);
13        if (isNaN(result)) {
14            throw new Error(`Invalid number: ${item}`);
15        }
16        console.log(`Parsed value: ${result}`);
17    } catch (error) {
18        console.error(`Error processing item: ${item}. ${error.message}`);
19    }
20}
  • In this example, error handling is performed for processing invalid data, detecting and reporting issues.

Points to note in asynchronous processing

When handling asynchronous processing in loops, using async/await can result in concise and intuitive code.

 1const urls = ["https://example.com/1", "https://example.com/2"];
 2
 3// Proper handling of asynchronous operations
 4async function fetchUrls() {
 5    for (const url of urls) {
 6        const response = await fetch(url);
 7        const data = await response.json();
 8        console.log(data);
 9    }
10}
11
12fetchUrls();
  • This code asynchronously fetches URLs from the urls array one by one and processes the results in JSON format. Using async/await simplifies asynchronous operations, sequentially retrieving data for each URL and outputting it to the console.

Understand the difference between for...of statement and forEach() in asynchronous processing

 1async function asyncTask(num) {
 2    return new Promise(resolve => {
 3        setTimeout(() => {
 4            console.log(`Task ${num} done`);
 5            resolve();
 6        }, 100);
 7    });
 8}
 9
10async function runWithForOf() {
11    console.log("Start for...of");
12    for (const num of [1, 2, 3]) {
13        await asyncTask(num);
14    }
15    console.log("End for...of");
16}
17
18async function runWithForEach() {
19    console.log("Start forEach");
20    [1, 2, 3].forEach(async num => {
21        await asyncTask(num);
22    });
23    console.log("End forEach");
24}
25
26async function executeExamples() {
27    await runWithForOf();
28    await runWithForEach();
29}
30
31executeExamples();
  • When handling asynchronous processing in loops, note the differences in behavior, as shown in this example, between using for...of with async/await and using forEach().

  • With for...of, the code executes sequentially and waits at the await inside the loop before continuing to the next iteration. On the other hand, forEach() executes the processing in parallel.

Conclusion

The for statement in JavaScript is a simple yet powerful tool. By leveraging the best practices introduced here, you can write efficient and highly readable code. Pay attention to selecting appropriate loop constructs, scope management, error handling, and aim for highly maintainable code.

You can follow along with the above article using Visual Studio Code on our YouTube channel. Please also check out the YouTube channel.

YouTube Video