複数の戻り値パターン
複数の戻り値パターン とは
関数からまとめて複数の値を返したいときは、オブジェクトに詰めて返そう。本レッスンでは、複数の戻り値パターン の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
1 つの関数で複数の値を返したい
関数を書いていると「return で 2 つ以上の値を返したい」というシーンが必ず出てきます。たとえば「最小値と最大値を両方返す」「商と余りを両方返す」「ユーザー情報をまとめて返す」など、関連する値はセットで欲しいものです。
ところが JavaScript の return 文は 1 つの値しか返せません。これは Java や Python でも同じです。じゃあどうするのかというと、1 つの「箱」にまとめて返す という発想で解決します。JavaScript でその箱の代表が オブジェクト です。
JavaScript
function divmod(a, b) {
return { quotient: Math.floor(a / b), remainder: a % b };
}
const result = divmod(17, 5);
console.log(result.quotient); // => 3
console.log(result.remainder); // => 2{ quotient: ..., remainder: ... } がオブジェクトです。{} で囲んで、キー: 値 のペアをカンマで区切って並べると、関連する値を 1 つの箱にまとめられます。
オブジェクトは
JavaScriptの心臓部です。配列もオブジェクトの一種、関数もオブジェクトの一種。「とにかく{}で何でも表現する」のがJavaScriptの世界観だと覚えておきましょう。
オブジェクトの基本形
オブジェクトの作り方をもう少し見てみます。
JavaScript
const user = {
name: "Alice",
age: 28,
isAdmin: false,
};
console.log(user.name); // => Alice
console.log(user["age"]); // => 28
console.log(user.isAdmin); // => false値を取り出すときは user.name のようにドット . で書くか、user["name"] のように角カッコ [] で書きます。動的にキーを決めたいときだけ []、それ以外はドット . を使うのが一般的です。
オブジェクトのキーは「フィールド」「プロパティ」「メンバー」など色々な呼び方をされますが、どれも同じものを指しています。最初は「キー」と覚えておけば十分です。
配列との使い分け
複数の値をまとめる入れ物には、もう 1 つ 配列 もあります。
JavaScript
function minMax(numbers) {
return [Math.min(...numbers), Math.max(...numbers)];
}
const [min, max] = minMax([3, 1, 4, 1, 5]);
console.log(min, max); // => 1 5配列は [] で囲み、順番に並べた値だけを持ちます。「最小、最大」のように 順序が明確 な 2 値ならスマートですが、[28, false, "Alice"] のように意味の違う値を並べると、後で「何番目が何だったっけ?」となりがちです。
そのため、3 つ以上の値や意味が違う値はオブジェクト、対称的な 2 値は配列、というのが目安になります。
返し方の流れを図で見る
オブジェクトで返す流れを、図で確認しておきましょう。
計算した複数の値をいったん {} に詰めて、return で渡し、呼び出し側で .キー名 で必要な値を取り出す、という流れになります。
分割代入で受け取る
オブジェクトで返ってきた値は、分割代入 という構文ですっきり取り出せます。
JavaScript
function profile(name, age) {
return { displayName: name, ageLabel: age + " 歳" };
}
const { displayName, ageLabel } = profile("Alice", 28);
console.log(displayName); // => Alice
console.log(ageLabel); // => 28 歳const { displayName, ageLabel } = ... という書き方で、オブジェクトのキーと同じ名前の変数に一気に代入できます。これも第 7 章でじっくり学ぶので、今は「便利な書き方があるんだな」程度で OK です。
オブジェクトを返す関数はテストもしやすくなります。「複数の戻り値を 1 つの構造として比較できる」からです。同じ理由で、
Reactのフックなども多くがオブジェクトを返す形になっています。
よくある間違い
オブジェクト返しでよくある落とし穴を 3 つ紹介します。
returnと{の間で改行する —JavaScriptの ASI のせいで、returnのすぐ後ろで改行するとreturn;と解釈されてundefinedが返ります。return {は同じ行に書くのが鉄則です- キーの引用符を間違える — オブジェクトリテラルのキーは引用符なしで書けますが、
name-with-dashのようなハイフンを含む名前はクォートが必要です。普段は引用符なしで十分です - キー名のタイポ —
result.quantityと書きたいのにresult.qantityと書くと、エラーにならずundefinedが返ります。型チェックがない世界では、タイポを発見しづらいので注意が必要です
オブジェクトを返す関数は「API のレスポンス」と思うとイメージしやすいです。クライアントは決まったキー名で値を取り出すので、キー名を変えると影響範囲が大きくなります。
やってみよう
それでは課題に挑戦しましょう。divmod という関数を作って、商 と 余り をオブジェクトにまとめて返します。
- 引数
aとbを受け取る Math.floor(a / b)で商を計算 (整数の商)a % bで余りを計算{ quotient: 商, remainder: 余り }の形でreturnする
divmod(17, 5) は { quotient: 3, remainder: 2 }、divmod(10, 3) は { quotient: 3, remainder: 1 } を返します。テストはオブジェクトの中身まで比較するので、キー名 (quotient と remainder) を正確に書いてください。
慣れてきたら、{ q: 3, r: 2 } のように短いキー名にしたり、配列 [3, 2] で返したりして、書き換えごとに「何が便利で何が不便か」を体感してみてください。
よくある質問
Q. ブラウザと Node.js で動作は変わりますか?
A. JavaScript の構文・組み込み機能(配列・文字列・Promise 等)はどちらでも同じです。違いは DOM API(ブラウザ専用)と fs などのファイル系(Node 専用)など、実行環境固有の機能です。コアロジックは両方で動くため、テストもしやすくなります。
Q. ES6 以降の新機能は使っても大丈夫ですか?
A. 現代のブラウザ・Node.js は ES2020 以降の機能をほぼサポートしています。古い IE 等を対象にする必要が無ければ、let/const、アロー関数、分割代入、async/await を積極的に使ってください。トランスパイル(Babel/Vite)が必要なケースは年々減っています。
Q. TypeScript に進む価値はありますか?
A. 型情報があるとリファクタやエディタ補完が劇的に効くため、規模が大きくなるなら TypeScript への移行を強くおすすめします。最初は any を許容しつつ徐々に型を付ければ、JS の知識をそのまま活かせます。
次のレッスン
次は JS が動く順番を意識する で、関数からまとめて複数の値を返したいときは、オブジェクトに詰めて返そう を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- オブジェクトで返す の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. オブジェクトで返す とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- 関数名は
divmod、引数はaとbの 2 つ - 戻り値は
{ quotient: 商, remainder: 余り }形式のオブジェクト - 商は
Math.floor(a / b)、余りはa % bで計算すること
入出力例
test-cases.txt
divmod(17, 5) → {"quotient":3,"remainder":2}
divmod(10, 3) → {"quotient":3,"remainder":1}
divmod(20, 4) → {"quotient":5,"remainder":0}
divmod(0, 7) → {"quotient":0,"remainder":0}