JavaScript | 可変長引数(rest パラメータ)

JavaScript JavaScript
スポンサーリンク

プログラミングを始めたばかりだと「…(ドット3つ)」が何をしているのか戸惑いますよね。rest パラメータは「引数の数が決まっていなくても関数で受け取れる」便利な仕組みです。例を交えながら、直感的に理解できるように説明します。


基本の考え方と書き方

  • 意味: 関数定義で ...名前 と書くと、余った引数を「配列」としてまとめて受け取れます。
  • 場所: rest は「最後の引数」しか置けません。
  • 数: rest は「1つだけ」しか定義できません。
function sum(...numbers) {
  // numbers は配列なので、for や reduce が使える
  return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3, 4)); // 10
JavaScript

rest で受け取った値は配列として扱えるため、map / filter / reduce などがそのまま使えます。また、仕様上、rest は最後に1つだけ、末尾のカンマ不可、デフォルト値不可などの制約があります。


固定引数+rest の実例

  • パターン: 最初のいくつかは「必須の固定引数」、残りは「まとめて」受け取る。
function greet(greeting, ...names) {
  return `${greeting}, ${names.join(' and ')}!`;
}

console.log(greet("Hello", "Alice", "Bob", "Charlie"));
// "Hello, Alice and Bob and Charlie!"
JavaScript

このように、固定の引数の後ろに rest を置くと、柔軟で読みやすい関数が書けます。


rest と「スプレッド構文」の違い

同じ「…」でも、使う場所で役割が変わります。

  • rest: 関数「定義」の引数で使い、余りを「集めて配列化」する。
  • spread: 関数「呼び出し」や配列リテラルで使い、配列やオブジェクトを「展開」する。
function multiply(x, y, z) {
  return x * y * z;
}

const numbers = [1, 2, 3];
console.log(multiply(...numbers)); // 6  ← 呼び出し側の「展開」が spread
JavaScript

配列を他の配列へ自然に差し込んだり、関数へ配列をそのまま渡すときに spread を使います。ES2015 以降では apply の代わりに spread が読みやすくて主流です Qiita Note


arguments との違い(初心者がつまずきやすい所)

  • arguments: 配列「風」オブジェクト。map などが直接使えない。アロー関数では存在しない。
  • rest: 本物の配列。map / filter / reduce がすぐ使える。アロー関数でも使える。
// arguments だと配列メソッドが使いづらい
function oldSum() {
  // [...arguments] として配列化が必要
  return [...arguments].reduce((t, n) => t + n, 0);
}

// rest なら最初から配列
const newSum = (...nums) => nums.reduce((t, n) => t + n, 0);
JavaScript

可読性と柔軟性の面で、今は rest の利用が推奨されます。


よくある使い方パターン

  • 可変長の合計や統計処理:
function average(...nums) {
  if (nums.length === 0) return 0;
  const sum = nums.reduce((t, n) => t + n, 0);
  return sum / nums.length;
}
JavaScript
  • 最初の引数が「設定」や「オプション」、残りがデータ:
function logWithLevel(level, ...messages) {
  console.log(`[${level}]`, ...messages);
}

logWithLevel("INFO", "Server started", { port: 8080 });
JavaScript
  • 第一引数を「基準」として、残りに同じ処理を適用:
function startsWith(prefix, ...texts) {
  return texts.map(text => text.startsWith(prefix));
}

console.log(startsWith("pre", "prefix", "preview", "next")); // [true, true, false]
JavaScript
  • コマンド風 API の設計(サブコマンド+引数群):
function run(cmd, ...args) {
  switch (cmd) {
    case "add":
      return args.reduce((t, n) => t + n, 0);
    case "mul":
      return args.reduce((t, n) => t * n, 1);
    default:
      throw new Error(`Unknown command: ${cmd}`);
  }
}

console.log(run("add", 1, 2, 3)); // 6
console.log(run("mul", 2, 3, 4)); // 24
JavaScript

rest で受け取った配列を使い回すことで、柔軟なインターフェースを簡潔に書けます。


仕様上の注意点(落とし穴を避ける)

  • 最後にしか置けない: function f(...rest, a) はエラー。function f(a, ...rest) は OK。
  • 1つだけ: function f(...a, ...b) は不可。
  • デフォルト値不可: function f(...rest = []) は不可。
  • 末尾カンマ不可: function f(...rest,) は不可。
  • 空呼び出しでも配列: 引数ゼロなら rest[] になる。
  • 広く動作: 主要ブラウザと Node.js で広く利用可能(ES2015 以降)。

手を動かす練習問題

  • 練習1: min/max
    • 課題: min(...nums)max(...nums) を rest で作成。
    • ヒント: Math.min(...nums) のように spread で展開。
  • 練習2: ユニーク化
    • 課題: unique(...items) が重複を除いた配列を返す。
    • ヒント: return [...new Set(items)];
  • 練習3: フォーマッタ
    • 課題: format(template, ...values)${0}, ${1} を values で置換。
    • ヒント: replace(/\$\{(\d+)\}/g, (_, i) => String(values[Number(i)] ?? ''))

直感メモ(覚え方)

  • rest は「受け皿」: 関数の中で余った引数を「配列にまとめる」。
  • spread は「取り分け」: 配列やオブジェクトを「ばらして渡す/差し込む」。
  • arguments より rest: モダン JS では配列の rest を使う方が自然で読みやすい。
タイトルとURLをコピーしました