【JavaScript】returnで呼び出し元に値を返す方法|戻り値の型・複数返却・早期return・アロー関数の省略まで解説

JavaScriptの return 文は「関数の結果を呼び出し元に渡す」ための基本構文ですが、使い方次第でコードの可読性・安全性・テスタブルさが大きく変わります。

この記事では基本的な戻り値の仕組みから、複数の値を返すパターン、早期returnによるガード節設計、アロー関数の暗黙的return、非同期関数での扱いまで、実践的なコードとともに解説します。

スポンサーリンク

return 文の基本

return 文は2つの役割を持ちます。①戻り値を返す、②その時点で関数の実行を終了させる。

return の基本
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(ガード節)と呼びます。

return がない場合・値を返さない場合

関数に return がない、または return; のみの場合、関数は undefined を返します。

return なし / 値なし return
// 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
1つの関数から複数の型を返すのは避けましょう。例えば成功時は数値、失敗時は 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 型パターン(成功/失敗を明示)

例外をスローする代わりに、成功・失敗を戻り値として表現するパターンです。エラー処理が明示的になります。

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); // "ゼロ除算はできません"
}
TypeScript を使っている場合:この Result 型パターンを型で厳密に表現できます。詳しくはTypeScriptのエラーハンドリング完全ガイドを参照してください。

早期 return(ガード節)

関数の冒頭で前提条件をチェックし、満たさない場合にすぐ return するパターンを早期return(またはガード節)と呼びます。深いネストを避け、コードの読みやすさを大幅に改善できます。

NG: ネストが深い
// 条件が増えるほど右方向に伸びていく
function processUser(user) {
  if (user) {
    if (user.isActive) {
      if (user.age >= 18) {
        return `${user.name}さんを処理しました`;
      } else {
        return '18歳未満は利用できません';
      }
    } else {
      return 'アカウントが無効です';
    }
  } else {
    return 'ユーザーが見つかりません';
  }
}
OK: 早期 return でフラット化
function processUser(user) {
  if (!user)              return 'ユーザーが見つかりません';
  if (!user.isActive)     return 'アカウントが無効です';
  if (user.age < 18)      return '18歳未満は利用できません';

  // ここに来た時点で user は有効・アクティブ・18歳以上
  return `${user.name}さんを処理しました`;
}
早期returnのメリット:「正常系のコード」がインデントなしで読める、条件の追加が容易、テストケースが明確になる。条件判定に変数を使う方法(ガード節・可読性)もあわせて参考にしてください。

バリデーション関数での活用

入力バリデーション
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)。

暗黙的 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 関数の return
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);
  }
}
早期 return を async 関数で使う
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

NG: return の後で改行するとundefinedになる
// BAD: return の直後に改行すると、ASI によって return; として扱われる
function getConfig() {
  return      // ← ここで自動セミコロンが挿入される
  {
    theme: 'dark',
    lang: 'ja',
  };
}

console.log(getConfig()); // undefined ← オブジェクトが返らない!
OK: return と値を同じ行にまとめる
function getConfig() {
  return {     // ← { を return と同じ行に書く
    theme: 'dark',
    lang: 'ja',
  };
}

console.log(getConfig()); // { theme: 'dark', lang: 'ja' }

forEach では return が呼び出し元まで届かない

NG: 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 ← 意図しない結果
OK: find / for…of を使う
// 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)

Qreturn がない関数は何を返しますか?
Aundefined を返します。JavaScriptではすべての関数は必ず値を返し、return がない場合は暗黙的に return undefined; が補われます。コンストラクタ(new 付き呼び出し)は例外で、return がなければ生成したインスタンスを返します。
Q関数から複数の値を返すベストな方法は?
A名前が必要な場合はオブジェクトreturn { x, y })、順序が明確な場合は配列return [min, max])が主流です。React の useState は配列で返しており、好きな変数名で受け取れます。TypeScript を使う場合は型で制約を加えると安全です。
Q早期 return は return 文を増やしすぎて逆に読みにくくなりませんか?
A「関数が短い(20行程度)」「ガード節はまとめて冒頭に書く」「1つの条件につき1つの return」を守れば、深いネストより断然読みやすくなります。ただし処理の途中に散らばる return は混乱を招くので、early return は冒頭の前提条件チェックだけに使うのが原則です。
Qアロー関数でオブジェクトを暗黙的に return するときによくミスします。対策は?
A() で包む習慣をつけることです。x => ({ key: x }) と書けばブロックと混同しません。また ESLint の arrow-body-style ルールを有効にすると、誤った省略形を自動検出できます。
Qasync 関数で return した値はどうなりますか?
Aasync 関数の 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と組み合わせた可読性向上テクニックは条件判定に変数を使う方法(ガード節)でも詳しく解説しています。