JavaScriptにおける開発において、関数はプログラムを構成する最小単位の部品です。
関数が特定の処理を実行し、その結果を呼び出し元に渡すための仕組みを「戻り値(返り値)」と呼びます。
戻り値を正しく理解し活用することは、コードの再利用性を高めるだけでなく、メンテナンス性の向上にも直結します。
本記事では、JavaScriptの戻り値に関する基礎知識から、実務で役立つ応用的なテクニックまでを詳しく解説します。
戻り値の基本概念と関数の役割
関数は入力を受け取り、何らかの処理を行い、最終的に出力を出すという一連の流れを持っています。
この「出力」に相当するのが戻り値であり、returnキーワードを使用して定義されます。
return文による値の返却
関数内でreturnが実行されると、その時点で関数の実行は終了します。
returnの右側に記述された式や値が、関数の呼び出し元に渡されます。
まずは、数値の加算を行うシンプルな関数の例を見てみましょう。
// 2つの引数を受け取り、その合計を戻り値として返す関数
function add(a, b) {
return a + b;
}
const result = add(5, 3);
console.log(result);
8
この例では、add(5, 3)が実行されると内部で8という数値が生成され、それが変数resultに代入されます。
戻り値を利用することで、計算結果を別の処理で再利用することが可能になります。
処理の中断としてのreturn
return文には、値を返すだけでなく「処理をその場で終わらせる」という重要な役割があります。
return以降に記述されたコードは、一切実行されません。
function checkNumber(num) {
if (num < 0) {
return "負の数です";
}
// 負の数の場合、ここから下の処理は実行されません
return "正の数、または0です";
}
console.log(checkNumber(-5));
console.log(checkNumber(10));
負の数です
正の数、または0です
このように、特定の条件を満たした時点で処理を切り上げる使い方は、条件分岐をシンプルに保つために非常に有効です。
暗黙的な戻り値とundefined
JavaScriptの関数では、return文を明示的に記述しなかった場合でも、実は戻り値が存在します。
明示的なreturnがない場合、関数は自動的にundefinedを返します。
function sayHello() {
console.log("こんにちは");
// return文がない
}
const greeting = sayHello();
console.log(greeting);
こんにちは
undefined
変数の値を更新するだけの処理や、画面にログを出すだけの関数であっても、戻り値を受け取ろうとすればundefinedが返ってくることを覚えておきましょう。
意図しないundefinedによるエラーを防ぐためには、関数が何を返すべきかを常に意識して設計することが重要です。
アロー関数における戻り値の省略記法
ES6から導入されたアロー関数では、処理が一行のみの場合に限り、returnキーワードと波括弧{}を省略することができます。
これにより、非常に簡潔にコードを記述することができます。
// 通常のアロー関数の書き方
const square = (x) => {
return x * x;
};
// 省略した書き方(暗黙的な戻り値)
const squareShort = (x) => x * x;
console.log(squareShort(4));
16
ただし、オブジェクトを直接返したい場合には注意が必要です。
波括弧{}が関数のブロックとして解釈されてしまうため、オブジェクトリテラルを返す際は丸括弧()で囲む必要があります。
// オブジェクトを返すアロー関数の省略形
const getUser = (name) => ({ name: name, role: "admin" });
console.log(getUser("田中"));
{ name: "田中", role: "admin" }
複数の値を戻り値として返す方法
通常、JavaScriptの関数は1つの戻り値しか返せませんが、配列やオブジェクトを活用することで複数のデータをまとめて返すことができます。
配列を用いた複数値の返却
データの順番が固定されている場合、配列を使って複数の値を返すのが便利です。
受け取り側で分割代入(Destructuring assignment)を使うと、コードがすっきりします。
function getCoordinates() {
const x = 100;
const y = 200;
return [x, y];
}
const [posX, posY] = getCoordinates();
console.log(`X座標: ${posX}, Y座標: ${posY}`);
X座標: 100, Y座標: 200
オブジェクトを用いた複数値の返却
戻り値に名前を付けて管理したい場合は、オブジェクトを返す方法が最も推奨されます。
オブジェクトを使用することで、呼び出し側でプロパティ名を使って直感的に値を取り出すことができます。
function fetchProduct() {
return {
id: 1,
name: "高性能ノートPC",
price: 150000
};
}
const { name, price } = fetchProduct();
console.log(`${name}の価格は${price}円です。`);
高性能ノートPCの価格は150000円です。
配列と異なり、オブジェクトであれば将来的に戻り値の項目が増えたとしても、既存のコードに影響を与えにくいというメリットがあります。
ガード節(アーリーリターン)による可読性の向上
複雑な条件分岐を持つ関数では、ネスト(入れ子)が深くなりがちです。
これを解消するためのテクニックが「ガード節」を用いたアーリーリターンです。
関数の冒頭で不正な値や例外的な条件をチェックし、すぐにreturnさせる手法です。
// ガード節を使わない例(ネストが深い)
function processData(data) {
if (data) {
if (data.isValid) {
// メインの処理
return "処理完了";
} else {
return "無効なデータです";
}
} else {
return "データが存在しません";
}
}
// ガード節を使った例(読みやすい)
function processDataEfficiently(data) {
if (!data) return "データが存在しません";
if (!data.isValid) return "無効なデータです";
// メインの処理をインデントせずに記述できる
return "処理完了";
}
アーリーリターンを意識することで、本来行いたいメインのロジックが際立ち、バグの混入を防ぐことができます。
非同期関数(Async/Await)における戻り値
現代のJavaScript開発において、非同期処理は避けて通れません。
asyncキーワードを付けた関数は、常にPromiseオブジェクトを戻り値として返します。
async function fetchData() {
return "データ取得成功";
}
const resultPromise = fetchData();
console.log(resultPromise);
resultPromise.then(val => console.log(val));
Promise { <fulfilled>: "データ取得成功" }
データ取得成功
たとえ関数内で文字列をreturnしていても、外部から見ればそれは「将来的に文字列が手に入る約束(Promise)」として扱われます。
値を取り出すためには、awaitを使用するか、.then()メソッドを繋げる必要があります。
高階関数と戻り値としての関数
JavaScriptでは、関数そのものを別の関数の戻り値として返すことができます。
これは「カリー化」や「クロージャ」といった高度なプログラミング手法の基盤となります。
function createMultiplier(multiplier) {
// 関数を戻り値として返す
return function(num) {
return num * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(10));
console.log(triple(10));
20
30
このように、特定のパラメータを固定した新しい関数を生成することで、コードの柔軟性が大幅に向上します。
戻り値の設計パターン比較
実務で関数の戻り値を設計する際、どのような形式を選択すべきか迷うことがあります。
状況に応じた適切な戻り値の形式を以下の表にまとめました。
| 戻り値の形式 | 主な用途 | メリット |
|---|---|---|
| 単一の値(数値・文字列) | 計算結果やフラグの返却 | 最もシンプルで直感的 |
| null / undefined | 「見つからなかった」等の状態提示 | 存在しないことを明示できる |
| 配列 | 座標やペアなど順序が重要な複数の値 | 分割代入で短く書ける |
| オブジェクト | 関連性の高い複数の属性情報 | 拡張性が高く、名前で意味が伝わる |
| Promise | API通信やファイル読み込みなどの非同期処理 | 完了を待ってから次の処理へ進める |
戻り値に関するよくあるミスとデバッグ
戻り値の扱いで初心者が陥りやすいミスの一つに、「returnの記述忘れ」があります。
特に複雑な条件分岐の中で、あるルートだけreturnを書き忘れると、予期せぬundefinedが伝播し、後続の処理でエラーを引き起こします。
function calculateDiscount(price, type) {
if (type === "member") {
return price * 0.8;
}
// ここでreturnし忘れると、非会員の場合にundefinedが返る
}
このような問題を防ぐためには、関数の最後に「デフォルトの戻り値」を配置するか、TypeScriptなどの静的型付けツールを導入して戻り値の型を明示することが推奨されます。
デバッグの際は、関数を呼び出している箇所の直前で戻り値を変数に格納し、console.logで型と値を確認する癖をつけましょう。
まとめ
JavaScriptにおける戻り値は、関数の出力を定義し、プログラム全体のデータの流れを制御するための重要な要素です。
単一の値を返す基本から、オブジェクトや配列を用いた高度な返却方法、そしてアロー関数や非同期処理における振る舞いまで、その活用範囲は多岐にわたります。
適切な戻り値の設計は、読みやすく再利用性の高いクリーンなコードを書くための第一歩です。
ガード節を利用したアーリーリターンを意識し、複雑なロジックを整理する習慣を身に付けていきましょう。
日々のコーディングの中で、自身の作成した関数が「何を呼び出し元に伝えるべきか」を常に意識してみてください。
基礎を固めることで、大規模なアプリケーション開発においても迷いのない設計ができるようになるはずです。
