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}forstatement is suitable when the number of iterations is predetermined.for...ofstatement is suitable for concisely processing arrays and iterable objects.for...instatement is used to iterate over an object's properties. However, it is not suitable for arrays.whilestatement anddo...whilestatement 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}forstatement allows explicit index management while iterating.forEachmethod uses a callback function to process each element concisely.for...ofstatement 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
querySelectormethod 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}varhas function scope, so after the loopiis3, and all functions executed bysetTimeoutoutput3.- Using
let, theiinside thesetTimeoutcallback function refers to a new value for each loop, so0, 1, 2are 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
breakallows you to terminate loop processing midway, skipping all subsequent iterations. - Using
continueallows 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
urlsarray one by one and processes the results in JSON format. Usingasync/awaitsimplifies 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...ofwithasync/awaitand usingforEach(). -
With
for...of, the code executes sequentially and waits at theawaitinside 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.