基礎

JavaScriptの残余引数(rest parameters)入門|可変長引数を配列で受け取る方法

残余引数(rest parameters)は、関数に渡された不定数の引数を配列としてまとめて受け取るES6の機能です。...(スプレッド構文と同じ記号)を引数名の前に付けて定義します。従来の arguments オブジェクトに代わる、より直感的で使いやすい方法です。

この記事では、残余引数の基本的な使い方から、通常の引数との組み合わせ、arguments との違い、実践的なユースケースまで解説します。

基本的な使い方

残余引数は、関数定義の最後の引数に ... を付けて宣言します。渡されたすべての引数(または残りの引数)が配列として格納されます。

JavaScript
// すべての引数を配列で受け取る
function sum(...numbers) {
  console.log('引数:', numbers);
  console.log('型:', Array.isArray(numbers));
  return numbers.reduce((total, n) => total + n, 0);
}

console.log(sum(1, 2, 3));
console.log(sum(10, 20, 30, 40, 50));
console.log(sum(5));
実行結果
引数: [1, 2, 3]
型: true
6
引数: [10, 20, 30, 40, 50]
型: true
150
引数: [5]
型: true
5

残余引数は本物の配列(Array)なので、mapfilterreduce などの配列メソッドをそのまま使えます。これが arguments オブジェクト(配列風オブジェクト)との大きな違いです。

通常の引数との組み合わせ

残余引数は、通常の引数の後に配置して、「残り」の引数だけをまとめて受け取ることができます。

JavaScript
// 最初の引数は個別に、残りを配列で受け取る
function introduce(greeting, ...names) {
  names.forEach(name => {
    console.log(greeting + '、' + name + 'さん!');
  });
}

introduce('こんにちは', '田中', '鈴木', '佐藤');

// 第1・第2引数は個別に、残りをまとめる
function createTeam(leader, subLeader, ...members) {
  console.log('リーダー:', leader);
  console.log('サブリーダー:', subLeader);
  console.log('メンバー:', members);
  console.log('チーム人数:', 2 + members.length);
}

createTeam('山田', '高橋', '伊藤', '渡辺', '中村');
実行結果
こんにちは、田中さん!
こんにちは、鈴木さん!
こんにちは、佐藤さん!
リーダー: 山田
サブリーダー: 高橋
メンバー: ['伊藤', '渡辺', '中村']
チーム人数: 5

通常の引数が先に割り当てられ、残りが ...rest に入ります。このパターンは、第1引数が処理の種類や設定で、残りがデータ、といった関数設計でよく使われます。

argumentsオブジェクトとの違い

JavaScript
// arguments(従来の方法)- 配列風オブジェクト
function oldWay() {
  console.log('arguments:', arguments);
  console.log('配列か:', Array.isArray(arguments));  // false
  // 配列メソッドを使うにはArray.fromが必要
  const args = Array.from(arguments);
  return args.reduce((sum, n) => sum + n, 0);
}
console.log(oldWay(1, 2, 3));

// rest parameters(新しい方法)- 本物の配列
const newWay = (...args) => {
  console.log('rest:', args);
  console.log('配列か:', Array.isArray(args));  // true
  return args.reduce((sum, n) => sum + n, 0);
};
console.log(newWay(1, 2, 3));
実行結果
arguments: [Arguments] { '0': 1, '1': 2, '2': 3 }
配列か: false
6
rest: [1, 2, 3]
配列か: true
6

arguments は配列風オブジェクトで、length やインデックスアクセスは使えますが、mapfilter は直接使えません。また、アロー関数では arguments が使えないため、アロー関数では残余引数を使う必要があります。

実践例

JavaScript
// ログ関数(レベル + 任意のメッセージ)
function log(level, ...messages) {
  const timestamp = new Date().toISOString();
  const prefix = '[' + timestamp + '] [' + level.toUpperCase() + ']';
  console.log(prefix, ...messages);
}

log('info', 'サーバー起動', 'ポート:', 3000);
log('error', 'データベース接続失敗');

// 関数合成(パイプ)
function pipe(...fns) {
  return (value) => fns.reduce((acc, fn) => fn(acc), value);
}

const processNumber = pipe(
  x => x * 2,
  x => x + 10,
  x => x.toString(),
  s => '結果: ' + s
);

console.log(processNumber(5));

// 配列のマージ(重複除去)
function mergeUnique(...arrays) {
  return [...new Set(arrays.flat())];
}

console.log(mergeUnique([1, 2, 3], [3, 4, 5], [5, 6, 1]));
実行結果
[2024-01-15T10:30:00.000Z] [INFO] サーバー起動 ポート: 3000
[2024-01-15T10:30:00.000Z] [ERROR] データベース接続失敗
結果: 20
[1, 2, 3, 4, 5, 6]
分割代入でも使える

残余構文は関数の引数だけでなく、分割代入でも使えます。const [first, ...rest] = [1, 2, 3, 4]const { id, ...others } = obj のように、配列やオブジェクトの分割代入で「残り」をまとめて取得できます。

残余引数は最後に1つだけ

残余引数は関数の引数リストの最後に1つだけ配置できます。function f(...a, ...b)function f(...a, b) はSyntaxErrorになります。「最初に固定引数、最後に残余引数」の順番を守りましょう。

まとめ

  • 残余引数 ...args は不定数の引数を本物の配列として受け取る
  • 通常の引数の後に置くと「残りの引数」だけがまとまる
  • arguments と違い、配列メソッドがそのまま使え、アロー関数でも動作する
  • ログ関数やパイプ関数など、引数の数が不定な場面で活躍する
  • 残余引数は引数リストの最後に1つだけ配置できる