JavaScriptにおける反復処理のベストプラクティス

JavaScriptにおける反復処理のベストプラクティス

この記事ではJavaScriptにおける反復処理のベストプラクティスについて説明します。

YouTube Video

JavaScriptにおける反復処理のベストプラクティス

JavaScriptでは、for文を使って反復処理を行うことが一般的です。ここでは、効率的で読みやすいコードを書くために知っておくべきfor文のベストプラクティスについて詳しく解説します。

適切なループ構文を選ぶ

JavaScriptには複数のループ構文があり、それぞれ異なる用途に適しています。

 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は、繰り返し回数が決まっている場合に適しています。
  • for...ofは、配列やイテラブルオブジェクトを簡潔に処理する場合に適しています。
  • for...inは、オブジェクトのプロパティを反復処理する場合に使用します。ただし、配列には適していません。
  • whileや**do...while文**は、条件に基づいてループを制御する場合に使用します。

forEachメソッドやfor...of文の活用

配列をループする場合、for文を使ってインデックスにアクセスすることが一般的ですが、forEachメソッドやfor...of文の方が読みやすい場合があります。

 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は、インデックスを明示的に管理しながら反復処理が可能です。
  • forEachメソッドは、コールバック関数を使い、簡潔な記述で各要素を処理できます。
  • for...ofは、可読性が高く、配列の各要素に直接アクセスできます。

ループ条件を効率化する

ループの条件式は繰り返し評価されるため、不要な計算を避けることでパフォーマンスを向上させることができます。

 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}
  • この例のように、事前に計算結果を変数に格納することでより効率的にループを実行できます。
 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}`);
  • この例のように、条件を逆順にすると効率的な場合があります。

ループ内の処理を効率化する

ループ内の処理は繰り返し実行されるため、不要な計算を避けることでパフォーマンスを向上させることができます。

 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}
  • この例では、querySelectorメソッドをループの外に移すことにより、不要な計算の重複を解消しています。

スコープを意識する

ループ内で使用する変数は、適切なスコープを持つようにletまたはconstを使用しましょう。varはスコープが関数に限定されるため、予期しない動作を引き起こす可能性があります。

 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 は関数スコープを持つため、ループ後も i3 になっており、setTimeout で実行される関数はすべて 3 を出力します。
  • let を使用すると、setTimeout のコールバック関数内の i はループごとに新しい値を参照するため、0, 1, 2 が期待通り出力されます。

早期終了で可読性を向上させる

ループの処理を簡潔にするために、breakcontinueを適切に使用して可読性を高めましょう。

 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}
  • breakを利用することによりループの処理を途中で終了し、それ以降の繰り返しを行いません。
  • continueを利用することにより、現在のループ処理をスキップし、次の繰り返しに移ります。

ネストを避ける

ネストが深くなるとコードが読みにくくなるため、早期リターンや関数分割を活用してネストを浅く保つことを心掛けましょう。

 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}
  • この例では、関数を利用してネストを浅くしています。

エラー処理を考慮する

ループ中にエラーが発生する可能性がある場合、適切なエラーハンドリングを実装しましょう。

 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}
  • この例では、無効なデータを処理する際にエラーハンドリングを行い、問題を検出して報告します。

非同期処理での注意点

非同期処理をループで扱う場合、async/awaitを活用すると簡潔で直感的なコードになります。

 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();
  • このコードでは、urls配列内のURLを1つずつ非同期にフェッチし、結果をJSON形式で処理しています。async/awaitを使用して非同期操作をシンプルに記述しており、各URLのデータを順番に取得してコンソールに出力します。

非同期処理でfor...of文とforEach()の違いを理解する

 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();
  • 非同期処理をループで扱う場合、この例のようにfor...of文とasync/awaitを活用した場合と、forEach()を活用した場合で動作が異なる点にも留意してください。

  • for...of文では、順番に実行され、ループ内の await により次の処理を待ちます。一方、forEach()では、処理が並列実行されます。

結論

JavaScriptのfor文は、シンプルながら強力なツールです。ここで紹介したベストプラクティスを活用すれば、効率的で可読性の高いコードを書くことができます。適切なループ構文の選択やスコープ管理、エラー処理などに注意を払い、メンテナンス性の高いコードを目指しましょう。

YouTubeチャンネルでは、Visual Studio Codeを用いて上記の記事を見ながら確認できます。 ぜひYouTubeチャンネルもご覧ください。

YouTube Video