JavaScript | ES6+ 文法:スプレッド構文 – 関数引数展開

JavaScript
スポンサーリンク

関数引数展開とは何か

スプレッド構文の「関数引数展開」は、fn(...array) のように書いて「配列(や配列っぽいもの)をバラして、関数の複数の引数として渡す」書き方です。
ここが重要です:... が「関数呼び出しのカッコの中(右側)」にあるときは、配列展開=要素を1個ずつ引数にする、という意味になります。

const nums = [1, 2, 3];

const sum = (a, b, c) => a + b + c;

console.log(sum(1, 2, 3));   // 6(普通に渡す)
console.log(sum(...nums));   // 6(配列を展開して渡す)
JavaScript

sum(...nums) は、実行時に「sum(1, 2, 3) と同じ」とみなされます。

従来の書き方との違い(apply からの進化)

Math.max / Math.min との組み合わせ

配列の最大値・最小値を取りたいとき、昔は apply を使っていました。

const nums = [5, 2, 9];

console.log(Math.max.apply(null, nums)); // ES5 の書き方
JavaScript

スプレッド構文なら、ここが重要です:Math.max(...nums) のように「見た目そのまま」で書けます。

const nums = [5, 2, 9];

console.log(Math.max(...nums)); // 9
console.log(Math.min(...nums)); // 2
JavaScript

apply に比べて、読みやすさ・書きやすさが圧倒的に良くなります。

自作関数でも同じ感覚で使える

自分で定義した関数にも、そのまま配列を展開して渡せます。

const diff = (a, b, c) => a - b - c;

const args = [10, 3, 2];

console.log(diff(...args)); // 10 - 3 - 2 = 5
JavaScript

部分的な引数+スプレッド(固定+可変の混在)

先頭だけ固定、残りは配列から

最初の引数だけ手書きし、残りは配列から展開する、という書き方が自然にできます。
ここが重要です:関数側のシグネチャ(引数リスト)を意識しつつ、「どこを固定し、どこを配列から渡すか」を決める。

const log = (level, a, b, c) => {
  console.log(level, a, b, c);
};

const rest = ["A", "B", "C"];

log("INFO", ...rest); // "INFO A B C"
JavaScript

可変長のメッセージをまとめて渡す

ログ関数などで、「先頭はレベル、それ以降は可変長引数」というパターンもよくあります。

const logMany = (level, ...messages) => {
  console.log(level, ...messages);
};

const msgs = ["start", "user=7", "ok"];

logMany("INFO", ...msgs); // "INFO start user=7 ok"
JavaScript

スプレッド引数と rest パラメータの違い(ここをしっかり)

ここが一番混ざりやすいポイントです。同じ ... でも、立場が逆です。

関数「定義側」の (a, ...rest) は、残りの引数をまとめて受け取る(rest パラメータ)。
関数「呼び出し側」の fn(...array) は、配列をバラして渡す(スプレッド)。

// 定義側:rest パラメータ(受け取る側)
function sumAll(...nums) {
  return nums.reduce((s, n) => s + n, 0);
}

// 呼び出し側:スプレッド(渡す側)
const arr = [1, 2, 3];
console.log(sumAll(...arr)); // 6
JavaScript

ここが重要です:

  • rest は「余りをまとめる(受ける)」
  • spread は「配列をバラす(渡す)」

「定義側か/呼び出し側か」で役割が真逆になる、と意識しておくと混乱しません。

実務でよく使うパターン(重要ポイントを深掘り)

既存の関数に配列をそのまま渡したい

既にある API が「可変長引数」を受ける形になっているとき(fn(a, b, c, ...))、配列を渡すにはスプレッドが必須になります。

const nums = [1, 5, 2, 9];

console.log(Math.max(...nums)); // 9
JavaScript

配列のまま Math.max(nums) と渡しても、「配列一個」しか引数にならないので動きません。
ここが重要です:「配列の中身を引数として扱いたいとき」は、必ず ... で展開する

引数リストを動的に組み立てる

条件によって「前後に追加の引数を足したい」ときも、スプレッドで配列をくっつけてから渡せます。

const callApi = (url, ...rest) => {
  console.log("url:", url);
  console.log("options:", rest);
};

const baseArgs = ["GET", { timeout: 3000 }];

// 先頭に URL を付けて展開
callApi("/users", ...baseArgs);
// 実質 callApi("/users", "GET", { timeout: 3000 })
JavaScript

「固定の前後に、可変の真ん中を挟む」ような場合も自然です。

const invoke = (...args) => console.log(...args);

const head = ["INFO"];
const body = ["A", "B"];
const tail = ["done"];

invoke(...head, ...body, ...tail);
// INFO A B done
JavaScript

よくある落とし穴と注意点

配列のまま渡してしまう

fn(arr)fn(...arr) の違いを意識していないと、想定外の振る舞いになります。

const sum = (a, b, c) => a + b + c;
const nums = [1, 2, 3];

console.log(sum(nums));     // "1,2,3undefinedundefined" に近い変な結果 or NaN
console.log(sum(...nums));  // 6(これが正しい)
JavaScript

ここが重要です:関数が「複数の引数」を期待しているのか、「配列1つ」を期待しているのかを、定義を見て判断する

引数が多すぎ/少なすぎる場合

スプレッドは「配列要素をそのまま順番に引数にする」だけなので、多すぎる引数は無視され、足りない引数は undefined になります。必要なら、関数側で rest パラメータを使ったり、既定値を指定したりして防御します。

const show = (a, b = 0, c = 0) => console.log(a, b, c);

show(...[1, 2, 3, 4]); // 1 2 3(4は捨てられる)
show(...[1]);          // 1 0 0(b,c は既定値)
JavaScript

「配列ではないもの」を展開しようとしてエラー

スプレッドは「反復可能(iterable)」なものに使えますが、初心者のうちは「とりあえず配列にだけ使う」と意識しておくと安全です。nullundefined に対して ... するとエラーになるので注意してください。

例題で理解を固める

// 1) 最大・最小・合計を計算する関数に配列をそのまま渡したい
const nums = [72, 88, 95, 64];

const max = Math.max(...nums);
const min = Math.min(...nums);
const sum = ((a, b, c, d) => a + b + c + d)(...nums);

console.log(max, min, sum); // 95 64 319

// 2) 自作関数で配列を展開
const greet = (first, middle, last) =>
  `Hello, ${first} ${middle} ${last}`;

const nameParts = ["Taro", "J.", "Yamada"];
console.log(greet(...nameParts));

// 3) 固定引数+配列展開
const logWithTag = (tag, ...msgs) => {
  console.log(tag, ...msgs);
};

const messages = ["start", "user=7", "ok"];
logWithTag("[APP]", ...messages);
// [APP] start user=7 ok

// 4) 関数ラッパーで引数を追加して渡す
const rawFetch = (...args) => console.log("fetch:", args);

const defaultHeaders = { "X-APP": "demo" };
const myFetch = (url, options = {}) => {
  const merged = { ...options, headers: { ...defaultHeaders, ...options.headers } };
  rawFetch(url, merged);
};

myFetch("/api", { method: "GET", headers: { "X-USER": "7" } });
JavaScript

まとめ

スプレッド構文の関数引数展開の核心は、「配列(や配列っぽいデータ)をバラして、関数の複数の引数として渡す」ことです。fn(...arr)fn(arr[0], arr[1], …) という展開。定義側の (...rest) は「まとめて受ける」、呼び出し側の (...array) は「バラして渡す」という役割の違いをしっかり区別してください。最大値・最小値の取得、ログ出力、既存関数への配列入力など、実務で頻出する場面で、スプレッドを使うとシンプルで読みやすい ES6+ コードになります。

タイトルとURLをコピーしました