早期リターン(early return)とは何か
early return は「不要な処理をなるべく“前で打ち切る”書き方」です。関数の先頭でガード(不正条件ならすぐ return)し、ループ内でも“用がない要素は早く抜ける/次へ進む”ことで、ネストを浅くし、読みやすさと安全性を高めます。ここが重要です:早期リターンは「肯定条件だけを深く書く」ための技法。否定条件を先に捨てると、残りのコードが“意図の中心”だけになります。
関数レベルの早期リターン(ガード節)
引数チェックを“先に捨てる”
function toLabels(products) {
if (!Array.isArray(products) || products.length === 0) return []; // ガード
return products
.filter(p => p && p.stock > 0)
.map(p => `${p.name} (${p.price})`);
}
JavaScriptここが重要です:不正や余計なケースは早めに返す。以降の処理は“正常系”だけに集中でき、ネストと条件分岐が減ります。
早期リターンでエラーをわかりやすく
function parsePrice(s) {
const n = Number(s);
if (!Number.isFinite(n) || n < 0) return null; // 早期終了
return Math.round(n);
}
JavaScriptここが重要です:“失敗は即返す”。成功経路だけを下に伸ばすと、テストも読みやすくなります。
ループ内の早期リターン的書き方(ガードと continue)
先に“不要”を弾いてから核心を書く
for (const p of products) {
if (p == null) continue; // 欠損はスキップ
if (p.stock <= 0) continue; // 在庫なしはスキップ
// ここから“やりたいこと”だけ
labels.push(`${p.name} (${p.price})`);
}
JavaScriptここが重要です:否定条件を先に捨てる(ガード)→本体は肯定条件だけ。ネストが浅く、ミスが減ります。
break と return の使い分け
function findFirstValid(xs) {
for (const x of xs) {
if (x == null) continue;
if (isValid(x)) return x; // 関数を“早期リターン”で終了
}
return null;
}
JavaScriptここが重要です:関数内なら“見つかったら return”。ループだけ止めたいなら break。意図を明確に分けます。
早期リターンを“配列メソッドで表現する”
find / some は“途中停止”を内蔵
const hit = products.find(p => p.stock > 0); // 最初の一致で止まる
const exists = products.some(p => p.price > 1000); // 条件成立で止まる
JavaScriptここが重要です:早期リターンの意図(最初の一致で終了)をメソッドで表せます。forEach では止められないので不適。
filter+map は“前で捨ててから変換”
const labels = products
.filter(p => p?.stock > 0) // 先に捨てる
.map(p => `${p.name} (${p.price})`); // 本体に集中
JavaScriptここが重要です:否定条件を filter に寄せると、本体(map)が平坦で理解しやすい。早期リターン的な発想です。
reduce でガードしながら1パス
const labels = products.reduce((acc, p) => {
if (!p || p.stock <= 0) return acc; // 先に捨てる
acc.push(`${p.name} (${p.price})`);
return acc;
}, []);
JavaScriptここが重要です:“捨てる→続行”の順で書くと、acc の扱いが安定し、意図が明確になります。
ネストの削減(早期リターンが効く場面の深掘り)
否定条件を先に“返す/続行する”
for (const row of rows) {
if (!row.enabled) continue; // 使わないなら即スキップ
if (row.value == null) continue; // 欠損はスキップ
process(row.value); // 核心だけを書く
}
JavaScriptここが重要です:if の中に処理を“入れない”。否定条件を早く捨てると、可読性が飛躍的に上がります。
多段条件は“早く確定するもの”から捨てる
- cheap な判定(null、型、範囲)を先に捨てる
- heavy な判定(正規表現、DB/ネット)を後に ここが重要です:コストの安いガードを手前に。平均計算量が下がります。
オブジェクト・ネスト構造での早期リターン
オプショナルチェーンで“無いなら捨てる”
for (const g of data.groups ?? []) {
const items = g.items ?? [];
if (items.length === 0) continue;
for (const it of items) {
if (!it?.id) continue;
ids.push(it.id);
}
}
JavaScriptここが重要です:存在しない経路は早くスキップ。下の処理は“あるものだけ”に集中できます。
再帰の早期リターン(存在しなければ即終了)
function walk(nodes, visit) {
if (!Array.isArray(nodes) || nodes.length === 0) return; // ガード
for (const n of nodes) {
visit(n);
if (Array.isArray(n.children)) walk(n.children, visit);
}
}
JavaScriptここが重要です:再帰でも同じ。無いなら即 return。以降は正常系のみ。
非同期処理での早期リターン(順次/並列)
順次で“失敗したら即終了”
async function pipeline(ids) {
const out = [];
for (const id of ids) {
const res = await fetch(`/api/${id}`);
if (!res.ok) return out; // 早期終了
out.push(await res.json());
}
return out;
}
JavaScript並列では“結果で分岐して捨てる”
const promises = ids.map(id => fetch(`/api/${id}`));
const results = await Promise.all(promises);
const okData = [];
for (const r of results) {
if (!r.ok) continue; // 失敗は捨てる
okData.push(await r.json());
}
JavaScriptここが重要です:await と早期リターンは for…of(順次)が相性良い。並列は“集めてから捨てる”発想に。
よくある落とし穴と回避策
forEach に早期停止を期待しない
forEach の return は“その要素だけ終了”。ループは続きます。止めたい/返したいなら for…of/find/some に切り替えます。
早期リターンで副作用を混ぜない
“返す前にちょっと書き換え”は事故の元。非破壊の原則を守り、必要なら“結果を新しい配列・オブジェクト”に。
ガードを曖昧にしない
“nullっぽい値を全部捨てる”なら、x == null(null と undefined)を使い、0 や “” を誤って捨てないようにします。
まとめ
early return は「不要なケースを先に捨てて、正常系だけを下に伸ばす」書き方です。関数ではガード節で即 return、ループでは否定条件を先に continue/早期 return。メソッドなら find/some/filter で“早く捨てる・早く止める”。重い判定は後ろへ、欠損は明確に弾き、非同期は for…of+await(順次)か Promise.all(並列)に設計を分ける。これを徹底すれば、初心者でも短くて読みやすく、意図がズレないロジックを書けます。
