forEachでcontinueが使えない理由と解決方法 - JavaScript初心者ガイド

JavaScript forEachメソッドでcontinue文が使えない理由と解決方法を詳しく解説。代替手段としてのreturn、for...of、filterメソッドの使い方を初心者向けに分かりやすく説明します。

Learning Next 運営
22 分で読めます

みなさん、JavaScriptで配列を処理していて困ったことはありませんか?

「forEachでcontinueを使いたいのにエラーになる」「配列の一部の要素をスキップしたいのに方法が分からない」

こんな経験をした初心者の方、きっと多いですよね。

実は、forEachメソッドには従来のforループとは異なる制約があります。 特にcontinue文が使えないという点で、多くの人が戸惑ってしまうんです。

この記事では、forEachでcontinueが使えない理由と、その解決方法について詳しく解説します。 代替手段も含めて、初心者にも分かりやすく説明していきますよ!

なぜforEachでcontinueが使えないの?

forEachの仕組みを理解しよう

forEachメソッドは関数型プログラミングのアプローチで動作します。

簡単に言うと、配列の各要素に対してコールバック関数を実行する仕組みなんです。 一方、continue文は従来のループ構文でのみ使用可能な制御文です。

この根本的な違いが、forEachでcontinueが使えない理由になります。

エラーになる例を見てみよう

let numbers = [1, 2, 3, 4, 5];
// これはエラーになります
numbers.forEach(function(number) {
if (number % 2 === 0) {
continue; // SyntaxError: Unexpected token 'continue'
}
console.log(number);
});

このコードを実行すると、構文エラーが発生します。 continue文がコールバック関数内で使えないためです。

なぜこうなるのか、内部的な仕組みを見てみましょう。

forEachの内部構造

// forEachは内部的にこのような仕組み
Array.prototype.forEach = function(callback) {
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this);
}
};

forEachは内部でforループを使って、各要素に対してコールバック関数を呼び出しています。 コールバック関数の中でcontinueを使おうとしても、それは関数内でのcontinueになってしまいます。

関数内でcontinueは使えないので、構文エラーになってしまうんです。

forEachで要素をスキップする方法

return文を使ってスキップ

forEachで要素をスキップしたい場合は、return文を使います。

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 偶数をスキップして奇数のみを処理
numbers.forEach(function(number) {
if (number % 2 === 0) {
return; // この反復をスキップ(continueの代替)
}
console.log(number); // 1, 3, 5, 7, 9
});

return文を使うことで、その反復をスキップできます。 continueと同じような効果が得られるんです。

アロー関数でも同じように書けます。

// アロー関数での書き方
numbers.forEach(number => {
if (number % 2 === 0) return;
console.log(number);
});

この方法がforEachでのスキップの基本テクニックです。

条件を逆転させる方法

もう一つの方法として、条件を逆転させることもできます。

let users = [
{ name: "太郎", age: 25, active: true },
{ name: "花子", age: 30, active: false },
{ name: "次郎", age: 35, active: true },
{ name: "美子", age: 28, active: false }
];
// return使用(スキップ方式)
users.forEach(user => {
if (!user.active) return;
console.log(`${user.name}さん(${user.age}歳)はアクティブです`);
});
// 条件を逆転(より自然)
users.forEach(user => {
if (user.active) {
console.log(`${user.name}さん(${user.age}歳)はアクティブです`);
}
});

条件を逆転させる方法の方が、コードが読みやすくなる場合があります。

複雑な条件でのスキップ

実際の開発では、より複雑な条件でスキップしたい場合もあります。

let products = [
{ name: "ノートPC", price: 80000, category: "electronics", inStock: true },
{ name: "デスク", price: 15000, category: "furniture", inStock: false },
{ name: "マウス", price: 2000, category: "electronics", inStock: true },
{ name: "椅子", price: 25000, category: "furniture", inStock: true }
];
// 複数条件でスキップ
products.forEach(product => {
// 在庫なしまたは価格が高すぎる場合はスキップ
if (!product.inStock || product.price > 50000) {
return;
}
console.log(`${product.name}: ${product.price}`);
});
// 出力: マウス: 2000円, 椅子: 25000円

複数の条件を組み合わせることで、柔軟なスキップ処理ができます。

代替手段を比較してみよう

for...ofループを使う方法

for...ofループなら、continue文をそのまま使えます。

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// for...ofでcontinueを使用
for (let number of numbers) {
if (number % 2 === 0) {
continue; // 正常に動作
}
console.log(number); // 1, 3, 5, 7, 9
}

for...ofループの利点は、従来のforループと同じ感覚で書けることです。 breakも使用可能なので、ループを完全に終了させることもできます。

// breakも使用可能
for (let number of numbers) {
if (number > 5) {
break; // ループを完全に終了
}
console.log(number); // 1, 2, 3, 4, 5
}

従来のforループ

最もシンプルで高速な方法は、従来のforループです。

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 従来のforループでcontinue使用
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 === 0) {
continue;
}
console.log(numbers[i]); // 1, 3, 5, 7, 9
}

インデックスも必要な場合は、この方法が便利です。

// インデックスも必要な場合
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 === 0) {
continue;
}
console.log(`インデックス${i}: ${numbers[i]}`);
}

filterメソッドとforEachの組み合わせ

関数型プログラミングのアプローチとして、filterとforEachを組み合わせる方法もあります。

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// filterで条件に合う要素を抽出してからforEach
numbers
.filter(number => number % 2 !== 0) // 奇数のみフィルタ
.forEach(number => {
console.log(number); // 1, 3, 5, 7, 9
});

この方法の利点は、処理の流れが明確になることです。 「フィルタしてから処理する」という意図が伝わりやすくなります。

より複雑な条件でも、段階的にフィルタできます。

products
.filter(product => product.inStock) // 在庫ありのみ
.filter(product => product.price <= 50000) // 価格が50000円以下
.forEach(product => {
console.log(`${product.name}: ${product.price}`);
});

実践的な使用例を見てみよう

ユーザーデータの処理

実際のWebアプリケーションでよくある、ユーザーデータの処理例を見てみましょう。

let users = [
{ id: 1, name: "田中太郎", email: "tanaka@example.com", verified: true },
{ id: 2, name: "佐藤花子", email: "", verified: false },
{ id: 3, name: "山田次郎", email: "yamada@example.com", verified: true },
{ id: 4, name: "鈴木美子", email: "suzuki@example.com", verified: false }
];
console.log("=== forEach + return ===");
users.forEach(user => {
// 未認証またはメール未設定の場合はスキップ
if (!user.verified || !user.email) {
return;
}
console.log(`${user.name}にメールを送信: ${user.email}`);
});

forEachとreturnを使った方法では、条件に合わない要素を簡単にスキップできます。

同じ処理をfor...ofでも書いてみましょう。

console.log("=== for...of + continue ===");
for (let user of users) {
if (!user.verified || !user.email) {
continue;
}
console.log(`${user.name}にメールを送信: ${user.email}`);
}

filterとforEachを組み合わせる方法も見てみましょう。

console.log("=== filter + forEach ===");
users
.filter(user => user.verified && user.email)
.forEach(user => {
console.log(`${user.name}にメールを送信: ${user.email}`);
});

どの方法も同じ結果が得られますが、それぞれに特徴があります。

ファイル処理のシミュレーション

ファイル処理のようなケースでの使い分けを見てみましょう。

let files = [
{ name: "document.txt", size: 1024, type: "text" },
{ name: "image.jpg", size: 5120000, type: "image" },
{ name: "video.mp4", size: 50000000, type: "video" },
{ name: "script.js", size: 2048, type: "text" },
{ name: "data.json", size: 512, type: "text" }
];
const MAX_FILE_SIZE = 10000000; // 10MB
// forEachでの処理(return使用)
console.log("=== forEach版 ===");
files.forEach(file => {
// ファイルサイズが大きすぎる場合はスキップ
if (file.size > MAX_FILE_SIZE) {
console.log(`⚠️ ${file.name} はサイズが大きすぎます`);
return;
}
// テキストファイル以外はスキップ
if (file.type !== "text") {
console.log(`⏭️ ${file.name} はテキストファイルではありません`);
return;
}
console.log(`${file.name} を処理しました`);
});

このように、複数の条件で段階的にスキップすることで、処理対象を絞り込めます。

どの方法を選ぶべき?

使い分けのガイドライン

どの方法を選ぶかは、状況によって異なります。

以下のガイドラインを参考にしてください。

  • forEach + return: 既存のforEachコードを簡単に修正したい場合
  • for...of + continue: 従来のforループに近い感覚で書きたい場合
  • filter + forEach: 関数型プログラミングアプローチを好む場合
  • 従来のfor: 最高のパフォーマンスが必要な場合

パフォーマンスの比較

大量のデータを処理する場合は、パフォーマンスも考慮しましょう。

let largeArray = Array.from({ length: 1000000 }, (_, i) => i + 1);
// パフォーマンステスト用の関数
function timeTest(name, fn) {
console.time(name);
fn();
console.timeEnd(name);
}
// forEach + return
timeTest("forEach + return", () => {
let sum = 0;
largeArray.forEach(num => {
if (num % 2 === 0) return;
sum += num;
});
});
// for...of + continue
timeTest("for...of + continue", () => {
let sum = 0;
for (let num of largeArray) {
if (num % 2 === 0) continue;
sum += num;
}
});
// 従来のforループ
timeTest("traditional for", () => {
let sum = 0;
for (let i = 0; i < largeArray.length; i++) {
if (largeArray[i] % 2 === 0) continue;
sum += largeArray[i];
}
});

一般的に、従来のforループが最も高速です。 パフォーマンスが重要な場合は、従来のforループを検討してみてください。

可読性の比較

コードの読みやすさも重要な要素です。

let orders = [
{ id: 1, amount: 1000, status: "completed", priority: "high" },
{ id: 2, amount: 500, status: "pending", priority: "low" },
{ id: 3, amount: 2000, status: "completed", priority: "high" },
{ id: 4, amount: 0, status: "cancelled", priority: "medium" },
{ id: 5, amount: 1500, status: "completed", priority: "high" }
];
// 方法1: forEach + return(段階的チェック)
orders.forEach(order => {
if (order.status !== "completed") return;
if (order.priority !== "high") return;
if (order.amount <= 0) return;
console.log(`注文 ${order.id}: ${order.amount}`);
});
// 方法2: filter + forEach(関数型アプローチ)
orders
.filter(order => order.status === "completed")
.filter(order => order.priority === "high")
.filter(order => order.amount > 0)
.forEach(order => {
console.log(`注文 ${order.id}: ${order.amount}`);
});

方法1は条件が一目で分かりやすく、方法2は処理の流れが明確です。 チームの好みや既存コードに合わせて選択しましょう。

よくある間違いと対策

breakをforEachで使おうとする

continueと同様に、breakもforEachでは使えません。

let numbers = [1, 2, 3, 4, 5];
// 間違い:forEachでbreakを使用
/*
numbers.forEach(number => {
if (number === 3) {
break; // SyntaxError
}
console.log(number);
});
*/
// 正しい方法1: for...ofを使用
for (let number of numbers) {
if (number === 3) {
break;
}
console.log(number);
}
// 正しい方法2: someメソッドを使用
numbers.some(number => {
if (number === 3) {
return true; // ループを終了
}
console.log(number);
return false; // ループを続行
});

ループを途中で終了したい場合は、for...ofやsomeメソッドを使いましょう。

条件が複雑になりすぎる

条件が複雑になると、コードが読みにくくなります。

let items = [
{ name: "A", type: "food", price: 100, available: true },
{ name: "B", type: "book", price: 0, available: false },
{ name: "C", type: "food", price: 200, available: true }
];
// 悪い例:複雑な条件
items.forEach(item => {
if (!(item.type === "food" && item.price > 0 && item.available)) {
return;
}
console.log(item.name);
});
// 良い例:段階的なチェック
items.forEach(item => {
if (item.type !== "food") return;
if (item.price <= 0) return;
if (!item.available) return;
console.log(item.name);
});
// 最良例:filterで事前に絞り込み
items
.filter(item => item.type === "food")
.filter(item => item.price > 0)
.filter(item => item.available)
.forEach(item => console.log(item.name));

条件が複雑な場合は、段階的にチェックするか、filterで事前に絞り込むのがおすすめです。

まとめ

forEachメソッドでのcontinue使用について詳しく解説しました。

重要なポイントをおさらいしましょう。

  • forEachではcontinue文は使用できない
  • return文で代替可能
  • for...ofやfilterメソッドが有効な代替手段
  • 用途に応じて適切な方法を選択することが大切

各方法の特徴も覚えておきましょう。

  • forEach + return: 既存コードの修正が簡単
  • for...of + continue: 従来のループに近い感覚
  • filter + forEach: 関数型プログラミングアプローチ
  • 従来のfor: 最高のパフォーマンス

選択の指針として、以下を参考にしてください。

  • 単純なスキップ処理:forEach + return
  • 複雑な制御が必要:for...ofまたは従来のfor
  • 関数型アプローチ:filter + forEach
  • パフォーマンス重視:従来のfor

今回学んだ知識を活用して、ぜひより効果的な配列処理を実装してみてください。 適切なループ手法を選択することで、読みやすく効率的なコードが書けるようになりますよ!

関連記事