JavaScript | ES6+ 文法:関数の進化 – 残余引数(…args)

JavaScript
スポンサーリンク

残余引数(…args)とは何か

残余引数(rest parameters)は、関数の仮引数に ...args のように書くと、渡された引数を“配列としてまとめて”受け取る仕組みです。ここが重要です:arguments と違って最初から本物の配列なので、mapfilterreduce を直に使えます。アロー関数は arguments を持たないため、可変長引数は ...args が正解です。

// 可変長の合計
const sum = (...nums) => nums.reduce((s, n) => s + n, 0);
sum(1, 2, 3); // 6

// 先頭は区切り文字、残りは結合対象
const joinWith = (sep, ...parts) => parts.join(sep);
joinWith("-", "a", "b", "c"); // "a-b-c"
JavaScript

使い方の基本ルール(位置・個数・型)

残余引数は“最後の1つ”だけ

ここが重要です:...args は必ず最後の引数に置き、1関数につき1つしか使えません。残余より後に通常引数を置くことはできません。

// OK
function f(a, b, ...rest) {}

// NG(構文エラー):rest の後に通常引数
// function g(a, ...rest, b) {}
JavaScript

型は混ざっても受け取れる(ただし意味は揃える)

配列として何でも入れられますが、読みやすさのために“同じ役割の値だけ”が入る設計にすると混乱しません。

const log = (tag, ...msgs) => console.log(`[${tag}]`, ...msgs);
log("info", "ready", { id: 1 }); // [info] ready { id: 1 }
JavaScript

spread(…)との違いと組み合わせ

rest は“受け取り側”、spread は“渡し側”

ここが重要です:... は2つの顔があります。パラメータ位置の ... は“まとめて受ける”rest、引数位置の ... は“配列を展開して渡す”spread。

const nums = [10, 20, 30];

// 受け取り側(rest)
const sum = (...xs) => xs.reduce((s, n) => s + n, 0);

// 渡し側(spread)
sum(...nums); // 60(配列を展開して渡す)
JavaScript

配列コピーや結合にも spread

渡し側の spread は、配列の非破壊コピーや結合にも使えます。rest とあわせて“柔軟な受け渡し”ができます。

const a = [1, 2], b = [3];
const c = [...a, ...b]; // [1, 2, 3]
JavaScript

デフォルト引数・分割代入との組み合わせ(安全設計)

先頭は固定、残りは任意

“文脈となる必須値”を先頭引数に置き、残余で可変部分を受けるとインターフェースが明快になります。

// イベント名は必須、ペイロードは任意
const emit = (name, ...payloads) => {
  console.log("event:", name, "payloads:", payloads.length);
};
emit("loaded");
emit("loaded", { id: 1 }, { cache: true });
JavaScript

オブジェクトで“意味”を明示する選択肢

残余が多様な意味を持つなら、オブジェクト引数+分割代入の方が読みやすくなります。

const render = (row, { rate = 0.1, round = Math.round, currency = "JPY" } = {}) =>
  `${row.id}:${round(row.price * (1 + rate))} ${currency}`;
JavaScript

配列メソッドとコールバックでの利点(現場で効く)

外部設定+要素を同時に扱う

コールバックの引数に“要素+残余の設定”を渡すパターンは読みやすく拡張しやすいです。

const applyTax = (row, rate = 0.1, round = Math.round) =>
  ({ id: row.id, priceWithTax: round(row.price * (1 + rate)) });

const withTax = (rows, ...args) => rows.map(r => applyTax(r, ...args));

const rows = [{ id: 1, price: 100 }, { id: 2, price: 200 }];
withTax(rows);                 // 既定(0.1, round)
withTax(rows, 0.08);           // 税率だけ差し替え
withTax(rows, 0.08, Math.ceil);// 丸め方も差し替え
JavaScript

ロガー・ディスパッチャーの柔軟化

“先頭で固定、後続は任意”はロギングやメッセージ配送に相性抜群です。

const logger = (tag, ...msgs) => console.log(`[${tag}]`, ...msgs);
logger("warn", "slow network", { retry: 3 });
JavaScript

よくある落とし穴と対策(重要ポイントの深掘り)

意味のバラバラな残余で“順序依存”が発生

残余に異なる意味の値を混ぜると、順序で壊れやすくなります。ここが重要です:同じ役割だけを残余に揃える。多様ならオブジェクト引数へ移行する。

// NG例(順序が壊れやすい)
// fn(...args) // args=[rate, round, currency] のような混在

// OK例(明示的なオブジェクト)
fn({ rate: 0.08, round: Math.ceil, currency: "JPY" });
JavaScript

毎回“重い値”を残余で渡してコスト増

新規関数や巨大オブジェクトを都度生成して渡すとパフォーマンスに響きます。共通のものは外で定義して参照を渡す。

const ceilRound = Math.ceil;
withTax(rows, 0.08, ceilRound); // 再利用
JavaScript

arguments を使おうとして失敗

アロー関数は arguments を持ちません。必ず ...args を使い、配列として扱いましょう。

// NG
// const concat = () => Array.from(arguments).join("");

// OK
const concat = (...parts) => parts.join("");
JavaScript

例題で理解を固める

// 1) 任意メッセージの整形
const format = (prefix, ...chunks) => [prefix, ...chunks].join(" | ");
format("INFO", "ready", "user=7"); // "INFO | ready | user=7"

// 2) 任意フィルタ+マップ:外部設定を残余で渡す
const transform = (rows, predicate = r => r.active, mapFn = r => r, ...extras) =>
  rows.filter(predicate).map(r => mapFn(r, ...extras));

const rows = [{ id: 1, active: true, price: 100 }, { id: 2, active: false, price: 200 }];
const view = transform(
  rows,
  undefined, // 既定のフィルタ(activeのみ)
  (r, rate = 0.1) => ({ id: r.id, tax: Math.round(r.price * rate) }),
  0.08 // 残余で rate を供給
);

// 3) 可変長の平均(非数値は除外)
const average = (...nums) => {
  const vals = nums.filter(n => Number.isFinite(n));
  return vals.length ? vals.reduce((s, n) => s + n, 0) / vals.length : 0;
};
average(10, 20, "x", 30); // 20
JavaScript

まとめ

残余引数(...args)の核心は「可変長の引数を配列として安全に受け取り、柔軟で読みやすいインターフェースを作る」ことです。最後の1つだけ・受け側は rest、渡し側は spread、先頭は固定で文脈を与える。意味が多様ならオブジェクト引数へ、重い値は再利用して渡す。arguments ではなく ...args を使う。この指針を徹底すれば、初心者でも可変長の関数やコールバックを直感的に設計・運用できます。

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