残余引数(…args)とは何か
残余引数(rest parameters)は、関数の仮引数に ...args のように書くと、渡された引数を“配列としてまとめて”受け取る仕組みです。ここが重要です:arguments と違って最初から本物の配列なので、map・filter・reduce を直に使えます。アロー関数は 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 }
JavaScriptspread(…)との違いと組み合わせ
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); // 再利用
JavaScriptarguments を使おうとして失敗
アロー関数は 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 を使う。この指針を徹底すれば、初心者でも可変長の関数やコールバックを直感的に設計・運用できます。
