JavaScriptにおける反復処理は、言語の進化とともに洗練されてきました。
かつての主流であった標準的なfor文やforEachメソッドに代わり、現代のフロントエンドおよびバックエンド開発において「for…of文」は最も直感的で強力なループ構文としての地位を確立しています。
2026年の開発環境では、単なる配列の走査に留まらず、非同期ストリームの処理や複雑なデータ構造の解析において、この構文をいかに使いこなすかがコードの品質を左右します。
本記事では、基礎から高度な応用手法までを具体的に提示し、現場で役立つ実装パターンを詳しく見ていきます。
for…of文の基礎と動作原理
for...of文は、ES6 (ECMAScript 2015) で導入された比較的新しい構文であり、「反復可能オブジェクト (iterable)」を走査するために設計されています。
従来のfor文のようにインデックスを管理する必要がなく、コードの可読性を劇的に向上させることが可能です。
反復可能プロトコルの理解
for...of文が動作する背景には、反復可能プロトコル (Iterable Protocol)という仕組みがあります。
これは、オブジェクトがSymbol.iteratorという特別なキーを持ち、それを通じてイテレータを返す仕組みのことです。
JavaScriptの標準的な組み込みオブジェクトの多くは、デフォルトでこのプロトコルを実装しています。
- Array (配列)
- String (文字列)
- Map
- Set
- TypedArray
- NodeList (DOM)
- arguments オブジェクト
基本的な構文
最もシンプルな配列の走査例を確認してみましょう。
// プログラミング言語の配列を定義
const languages = ['JavaScript', 'TypeScript', 'Rust', 'Go'];
// for...of文による反復処理
for (const lang of languages) {
console.log(`言語名: ${lang}`);
}
言語名: JavaScript
言語名: TypeScript
言語名: Rust
言語名: Go
このコードの利点は、配列の要素そのものに直接アクセスできる点にあります。
インデックス変数 i を作成し、languages[i] のように参照する手間が省けるため、オフバイワン・エラー (境界値のミス) を防ぐことができます。
配列処理における高度なテクニック
配列の処理において、単純に値を一つずつ取り出すだけでなく、インデックスが必要な場面や、特定の条件でループを制御したい場面があります。
entries()メソッドによるインデックスの取得
for...of文でインデックス(添字)も同時に取得したい場合は、entries()メソッドを組み合わせるのが最適です。
const fruits = ['Apple', 'Banana', 'Orange'];
// 配列のインデックスと値を分割代入で受け取る
for (const [index, fruit] of fruits.entries()) {
console.log(`${index + 1}番目の果物: ${fruit}`);
}
1番目の果物: Apple
2番目の果物: Banana
3番目の果物: Orange
分割代入を活用することで、コードが非常にスッキリとした印象になります。これは、forEachメソッドで第2引数にインデックスを受け取る動作と似ていますが、for...of文では後述するbreakやcontinueが使えるという大きな違いがあります。
ループの制御:breakとcontinue
forEachメソッドなどの高階関数では、ループの途中で処理を中断 (break) したり、現在の反復をスキップ (continue) したりすることができません。
制御構造を柔軟に扱える点が、for...of文を選択する強力な動機となります。
const numbers = [10, 25, 40, 5, 60, 15];
for (const num of numbers) {
// 30より大きい数値が見つかったら終了
if (num > 30) {
console.log(`${num}が見つかったので処理を中断します`);
break;
}
// 奇数はスキップして出力しない(この配列に奇数はないが例として)
if (num % 2 !== 0) {
continue;
}
console.log(`処理中の数値: ${num}`);
}
処理中の数値: 10
処理中の数値: 25
40が見つかったので処理を中断します
オブジェクトの反復処理
JavaScriptにおいて、通常の「プレーンなオブジェクト ({} )」は反復可能ではありません。
そのため、オブジェクトに対して直接 for...of を使用しようとすると、TypeError: object is not iterable というエラーが発生します。
しかし、2026年のモダンな開発では、ユーティリティメソッドを組み合わせてオブジェクトを効率的に処理します。
Object.keys/values/entriesとの組み合わせ
オブジェクトの中身を走査するには、静的メソッドを使用して配列に変換してからループを回します。
| メソッド | 取得できる内容 |
|---|---|
Object.keys(obj) | プロパティ名の配列 |
Object.values(obj) | プロパティ値の配列 |
Object.entries(obj) | [キー, 値] のペアの配列 |
const userSettings = {
theme: 'dark',
notifications: true,
fontSize: 16
};
// キーと値のペアをループする
for (const [key, value] of Object.entries(userSettings)) {
console.log(`設定項目: ${key}, 設定値: ${value}`);
}
設定項目: theme, 設定値: dark
設定項目: notifications, 設定値: true
設定項目: fontSize, 設定値: 16
MapとSetの走査
MapやSetは標準で反復可能であるため、変換なしで直接 for...of を利用できます。
const tagSet = new Set(['JavaScript', 'Web', 'Programming']);
for (const tag of tagSet) {
console.log(`タグ: ${tag}`);
}
const configMap = new Map([
['api_url', 'https://api.example.com'],
['timeout', 5000]
]);
for (const [key, val] of configMap) {
console.log(`${key}は${val}に設定されています`);
}
タグ: JavaScript
タグ: Web
タグ: Programming
api_urlはhttps://api.example.comに設定されています
timeoutは5000に設定されています
for await…of文による非同期処理の応用
モダンJavaScriptにおいて最も重要と言えるのが、非同期イテレーションです。
複数のAPIリクエストを順次実行したり、ストリームデータを処理したりする場合に for await...of 文が活躍します。
非同期ジェネレータとの組み合わせ
非同期ジェネレータは、yield を使って値を非同期的に生成します。
// 数秒ごとにデータを取得するシミュレーション
async function* fetchDataStream(items) {
for (const item of items) {
// 1秒待機
await new Promise(resolve => setTimeout(resolve, 1000));
yield `取得済みデータ: ${item}`;
}
}
async function processData() {
const dataItems = ['Data-A', 'Data-B', 'Data-C'];
console.log('ストリーム処理を開始します...');
// 非同期イテレータをループで回す
for await (const result of fetchDataStream(dataItems)) {
console.log(result);
}
console.log('すべての処理が完了しました');
}
processData();
実行結果(各行が約1秒おきに表示):
ストリーム処理を開始します...
取得済みデータ: Data-A
取得済みデータ: Data-B
取得済みデータ: Data-C
すべての処理が完了しました
この構文を使用することで、複雑な非同期処理を、あたかも同期処理のようなシンプルな見た目で記述できるようになります。
Fetch APIとの連携
2026年現在のブラウザ環境やNode.js環境では、ReadableStream を非同期に反復処理することも一般的です。
async function readResponseStream(response) {
const reader = response.body;
// ReadableStreamが非同期反復に対応している場合
for await (const chunk of reader) {
console.log(`受信データサイズ: ${chunk.length} bytes`);
}
}
性能比較と使い分けのガイドライン
開発者が直面する疑問の一つに、「for...of は forEach や従来の for 文よりも遅いのか?」というものがあります。
パフォーマンスの真実
厳密なベンチマークテストでは、従来のインデックスベースの for 文が最も高速であることが多いです。
これは、イテレータオブジェクトの生成コストがかからないためです。
しかし、現代のJavaScriptエンジン(V8など)は for…of を高度に最適化しており、ほとんどのビジネスロジックにおいてその差は無視できるレベルです。
むしろ、パフォーマンスよりもコードの保守性とバグの少なさを優先すべきです。
使い分けの基準
for…ofを使うべき場面
- 配列やMapなどの全要素を走査する場合。
- ループの途中で
breakやreturnを行いたい場合。 - 非同期処理 (
await) をループ内で行いたい場合。
forEachを使うべき場面
- 関数型のプログラミングスタイルを好む場合。
- 配列を書き換えたり、チェーンメソッドの一部として記述したい場合(ただし可読性に注意)。
for(let i=0;…) を使うべき場面
- 膨大な(数百万要素以上の)データをミリ秒単位で高速処理する必要がある極限のケース。
- 複雑なインデックス操作(2つ飛ばしで処理するなど)が必要な場合。
for…in文との決定的な違い
初心者からベテランまで時折混同してしまうのが for...in 文です。
この2つは似て非なるものです。
for...in: オブジェクトの「列挙可能なプロパティ名 (キー)」を走査する。for...of: オブジェクトの「値」を走査する。
配列に対して for...in を使用すると、予期せぬ挙動(プロトタイプチェーンに継承されたプロパティまで取得してしまうなど)が発生する可能性があるため、配列の走査に for…in を使用することは推奨されません。
const arr = [10, 20, 30];
arr.customProp = 'Extra';
console.log('--- for...in ---');
for (const key in arr) {
console.log(key); // インデックスだけでなく追加プロパティも出る可能性がある
}
console.log('--- for...of ---');
for (const value of arr) {
console.log(value); // 値だけが正しく出る
}
--- for...in ---
0
1
2
customProp
--- for...of ---
10
20
30
実践:複雑なデータ構造の解析
現場では、APIから取得したネストされたデータを処理することが多々あります。
for...of と分割代入を組み合わせることで、深い階層のデータもエレガントに扱えます。
const userList = [
{ id: 1, profile: { name: 'Alice', role: 'admin' } },
{ id: 2, profile: { name: 'Bob', role: 'user' } },
{ id: 3, profile: { name: 'Charlie', role: 'user' } }
];
// 入れ子になったオブジェクトを分割代入で直接抽出
for (const { id, profile: { name, role } } of userList) {
if (role === 'admin') {
console.log(`管理者発見: ${name} (ID: ${id})`);
} else {
console.log(`一般ユーザー: ${name}`);
}
}
管理者発見: Alice (ID: 1)
一般ユーザー: Bob
一般ユーザー: Charlie
このように、ループの宣言部分で必要なデータだけをピンポイントで取り出す手法は、2026年のJavaScript開発において標準的なベストプラクティスとなっています。
コードの意図が明確になり、一時的な変数を減らすことができるため、バグの混入を防ぐ効果があります。
まとめ
JavaScriptの for...of 文は、単なるループのための構文を超え、反復可能オブジェクトを効率的かつ安全に扱うための強力なツールです。
- 配列の走査においては、インデックスを意識せず安全に要素を処理でき、
entries()を使えば必要に応じてインデックスも取得可能です。 - オブジェクトの処理では、
Object.entries()と組み合わせることで、直感的なキー・バリューの操作が実現します。 - 非同期処理においては、
for await...ofを活用することで、複雑なプロミスの連鎖を排除し、同期的な見た目のクリーンなコードが書けます。 - 制御の柔軟性として、
breakやcontinueを使用した緻密なループ制御が可能です。
パフォーマンスの最適化が進んだ現代のエンジンでは、速度面での懸念もほぼ解消されています。
意味的に正しく、読み手にとって分かりやすいコードを書くために、まずは for...of を第一選択肢として検討してみてください。
日々の開発におけるコードの可読性とメンテナンス性が大きく向上することを実感できるはずです。
