JavaScriptの return 文は「関数の結果を呼び出し元に渡す」ための基本構文ですが、使い方次第でコードの可読性・安全性・テスタブルさが大きく変わります。
この記事では基本的な戻り値の仕組みから、複数の値を返すパターン、早期returnによるガード節設計、アロー関数の暗黙的return、非同期関数での扱いまで、実践的なコードとともに解説します。
return 文の基本
return 文は2つの役割を持ちます。①戻り値を返す、②その時点で関数の実行を終了させる。
function add(a, b) {
return a + b; // 計算結果を返しつつ関数を終了
}
const result = add(3, 5);
console.log(result); // 8
// return 以降のコードは実行されない
function greet(name) {
return `こんにちは、${name}さん!`;
console.log('この行は実行されない'); // 到達不能コード(dead code)
}
return がない場合・値を返さない場合
関数に return がない、または return; のみの場合、関数は undefined を返します。
// return がない関数
function logMessage(msg) {
console.log(msg);
// return 文がないので undefined を返す
}
const val = logMessage('テスト'); // コンソール: "テスト"
console.log(val); // undefined
// return; のみ(値なし return)
function checkAge(age) {
if (age < 0) return; // 不正値は何もせず終了(undefined を返す)
console.log(`年齢: ${age}歳`);
}
checkAge(-1); // 何も出力されない
checkAge(25); // "年齢: 25歳"
| パターン | 戻り値 | 使い所 |
|---|---|---|
return 値; |
指定した値 | 計算結果・変換結果を返すとき |
return; |
undefined |
早期終了(ガード節) |
| return なし | undefined |
副作用だけが目的の関数 |
戻り値の型
JavaScriptは動的型付けのため、関数はあらゆる型の値を返せます。
// 数値
function square(n) { return n * n; }
console.log(square(4)); // 16
// 文字列
function formatDate(date) {
return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`;
}
console.log(formatDate(new Date('2026-04-05'))); // "2026年4月5日"
// 真偽値
function isEven(n) { return n % 2 === 0; }
console.log(isEven(4)); // true
// 配列
function range(start, end) {
return Array.from({ length: end - start }, (_, i) => start + i);
}
console.log(range(1, 6)); // [1, 2, 3, 4, 5]
// オブジェクト
function parseQueryString(qs) {
return Object.fromEntries(new URLSearchParams(qs));
}
console.log(parseQueryString('name=田中&age=30')); // { name: '田中', age: '30' }
// 関数(高階関数)
function makeAdder(n) {
return (x) => x + n;
}
const add5 = makeAdder(5);
console.log(add5(10)); // 15
false を返す設計はバグを生みやすいです。代わりに後述の「オブジェクトで複数の値を返す」パターンや例外スローを使います。複数の値を返すパターン
JavaScriptの return は1つの値しか返せません。複数の値を返したいときはオブジェクトか配列にまとめます。
オブジェクトで返す(名前付き)
function analyzeText(text) {
const words = text.trim().split(/\s+/);
const chars = text.replace(/\s/g, '').length;
const lines = text.split('
').length;
const wordFreq = words.reduce((acc, w) => {
acc[w] = (acc[w] ?? 0) + 1;
return acc;
}, {});
return { wordCount: words.length, charCount: chars, lineCount: lines, wordFreq };
}
const { wordCount, charCount } = analyzeText('hello world hello');
console.log(wordCount); // 3
console.log(charCount); // 15
配列で返す(分割代入でシンプルに受け取る)
function minMax(numbers) {
const sorted = [...numbers].sort((a, b) => a - b);
return [sorted[0], sorted.at(-1)];
}
const [min, max] = minMax([3, 1, 4, 1, 5, 9, 2, 6]);
console.log(min, max); // 1 9
Result 型パターン(成功/失敗を明示)
例外をスローする代わりに、成功・失敗を戻り値として表現するパターンです。エラー処理が明示的になります。
function safeDivide(a, b) {
if (b === 0) {
return { ok: false, error: 'ゼロ除算はできません' };
}
return { ok: true, value: a / b };
}
const result = safeDivide(10, 3);
if (result.ok) {
console.log(result.value.toFixed(2)); // "3.33"
} else {
console.error(result.error);
}
const bad = safeDivide(5, 0);
if (!bad.ok) {
console.error(bad.error); // "ゼロ除算はできません"
}
早期 return(ガード節)
関数の冒頭で前提条件をチェックし、満たさない場合にすぐ return するパターンを早期return(またはガード節)と呼びます。深いネストを避け、コードの読みやすさを大幅に改善できます。
// 条件が増えるほど右方向に伸びていく
function processUser(user) {
if (user) {
if (user.isActive) {
if (user.age >= 18) {
return `${user.name}さんを処理しました`;
} else {
return '18歳未満は利用できません';
}
} else {
return 'アカウントが無効です';
}
} else {
return 'ユーザーが見つかりません';
}
}
function processUser(user) {
if (!user) return 'ユーザーが見つかりません';
if (!user.isActive) return 'アカウントが無効です';
if (user.age < 18) return '18歳未満は利用できません';
// ここに来た時点で user は有効・アクティブ・18歳以上
return `${user.name}さんを処理しました`;
}
バリデーション関数での活用
function validatePassword(password) {
if (typeof password !== 'string') return { valid: false, message: '文字列で入力してください' };
if (password.length < 8) return { valid: false, message: '8文字以上必要です' };
if (!/[A-Z]/.test(password)) return { valid: false, message: '大文字を含めてください' };
if (!/[0-9]/.test(password)) return { valid: false, message: '数字を含めてください' };
return { valid: true, message: 'OK' };
}
console.log(validatePassword('abc')); // { valid: false, message: '8文字以上必要です' }
console.log(validatePassword('abcdefgh')); // { valid: false, message: '大文字を含めてください' }
console.log(validatePassword('Abcdef12')); // { valid: true, message: 'OK' }
アロー関数の暗黙的 return
アロー関数では、関数本体が単一の式の場合に {} と return を省略できます(暗黙的 return)。
// 通常のアロー関数
const double = (x) => { return x * 2; };
// 暗黙的 return(同じ意味)
const double2 = (x) => x * 2;
// 配列の操作でよく使う
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8, 10]
const evens = numbers.filter(n => n % 2 === 0); // [2, 4]
// オブジェクトを返す場合は () で包む必要がある
// {} がブロックと解釈されてしまうため
const toObj = (x) => ({ value: x, doubled: x * 2 });
console.log(toObj(5)); // { value: 5, doubled: 10 }
// NG: {} がブロックとして解釈される(undefined が返る)
const brokenToObj = (x) => { value: x }; // SyntaxError にはならないが undefined を返す
() で包むのを忘れずに。=> { key: value } と書くと {} がブロックとして解釈され、undefined が返ります。非同期関数(async/await)の戻り値
async 関数の return は自動的に Promise でラップされます。
async function fetchUser(id) {
const response = await fetch(`https://api.example.com/users/${id}`);
if (!response.ok) {
// throw すると reject された Promise になる
throw new Error(`HTTPエラー: ${response.status}`);
}
// return した値は resolve された Promise になる
return response.json();
}
// 呼び出し側
fetchUser(1)
.then(user => console.log(user.name))
.catch(err => console.error(err.message));
// または await で受け取る
async function main() {
try {
const user = await fetchUser(1); // return した値が直接取れる
console.log(user.name);
} catch (err) {
console.error(err.message);
}
}
async function processOrder(orderId) {
if (!orderId) return null; // 早期 return は async 関数でも使える
const order = await fetchOrder(orderId);
if (!order) return { status: 'not_found' };
if (order.isPaid) return { status: 'already_paid' };
await chargePayment(order);
return { status: 'success', orderId };
}
return でよくあるミス
自動セミコロン挿入(ASI)による意図しない return
// BAD: return の直後に改行すると、ASI によって return; として扱われる
function getConfig() {
return // ← ここで自動セミコロンが挿入される
{
theme: 'dark',
lang: 'ja',
};
}
console.log(getConfig()); // undefined ← オブジェクトが返らない!
function getConfig() {
return { // ← { を return と同じ行に書く
theme: 'dark',
lang: 'ja',
};
}
console.log(getConfig()); // { theme: 'dark', lang: 'ja' }
forEach では return が呼び出し元まで届かない
// BAD: forEach のコールバックの return は forEach を抜けるだけ
function findFirst(arr, target) {
arr.forEach((item) => {
if (item === target) return item; // findFirst には届かない
});
// 常に undefined が返る
}
console.log(findFirst([1, 2, 3], 2)); // undefined ← 意図しない結果
// find を使う
function findFirst(arr, target) {
return arr.find((item) => item === target) ?? null;
}
// for...of を使う(早期 return が使える)
function findFirst2(arr, target) {
for (const item of arr) {
if (item === target) return item;
}
return null;
}
console.log(findFirst([1, 2, 3], 2)); // 2
console.log(findFirst2([1, 2, 3], 2)); // 2
よくある質問(FAQ)
undefined を返します。JavaScriptではすべての関数は必ず値を返し、return がない場合は暗黙的に return undefined; が補われます。コンストラクタ(new 付き呼び出し)は例外で、return がなければ生成したインスタンスを返します。return { x, y })、順序が明確な場合は配列(return [min, max])が主流です。React の useState は配列で返しており、好きな変数名で受け取れます。TypeScript を使う場合は型で制約を加えると安全です。() で包む習慣をつけることです。x => ({ key: x }) と書けばブロックと混同しません。また ESLint の arrow-body-style ルールを有効にすると、誤った省略形を自動検出できます。async 関数の return は自動的に Promise.resolve(値) でラップされます。呼び出し元は await または .then() で値を取り出します。throw すると Promise.reject(エラー) になります。まとめ
return 文に関する要点を整理します。
| トピック | ポイント |
|---|---|
| return なし | 暗黙的に undefined を返す |
| 複数の値を返す | オブジェクト(名前付き)または配列(分割代入)でまとめて返す |
| 早期 return | 冒頭のガード節でネストをフラット化。正常系が読みやすくなる |
| アロー関数の省略 | 単一式は暗黙的 return 可。オブジェクトは () で包む |
| ASI 罠 | return の直後で改行しない。{ は同じ行に書く |
| forEach の落とし穴 | コールバック内の return は外に出ない。find / for...of を使う |
| async 関数 | return した値は自動で Promise にラップされる |
関数全体の設計についてはJavaScriptの関数の基本と使い方で、早期returnと組み合わせた可読性向上テクニックは条件判定に変数を使う方法(ガード節)でも詳しく解説しています。
