スプレッド構文の「配列展開」とは何か
スプレッド構文の配列展開は、...array の形で「配列をバラして、その場に1個ずつ並べる」書き方です。ここが重要です:[...arr] と書くと「arr の中身だけ」が新しい配列にコピーされ、fn(...arr) と書くと「配列の要素を“複数の引数”として渡す」動きになります。... は「配列(反復可能なもの)を展開するための記号」と覚えてください。
const arr = [1, 2, 3];
console.log(...arr); // 1 2 3 (バラして並べる)
const copy = [...arr]; // [1, 2, 3](中身コピー)
const plus = (a, b, c) => a + b + c;
console.log(plus(...arr)); // 6 (引数として展開)
JavaScript配列コピーとしてのスプレッド(元配列を壊さない)
「代入」ではなく「コピー」を作る
const copy = arr; は“同じ配列を指す別名”になるだけで、片方を変更するともう片方にも影響します。ここが重要です:const copy = [...arr]; と書くと、中身をコピーした「別の配列」が作られるので、元配列を安全に保ったまま操作できます(浅いコピー)。
const arr = [1, 2, 3];
const alias = arr;
const copy = [...arr];
alias[0] = 99;
console.log(arr); // [99, 2, 3] ← alias は同じ配列
console.log(copy); // [1, 2, 3] ← スプレッドで作ったコピーはそのまま
JavaScript浅いコピーなので、配列の中にオブジェクトが入っている場合、そのオブジェクト自体は共有される点に注意してください。
const users = [{ name: "Alice" }, { name: "Bob" }];
const copyUsers = [...users];
copyUsers[0].name = "Charlie";
console.log(users[0].name); // "Charlie"(中のオブジェクトは共有)
console.log(copyUsers[0].name); // "Charlie"
JavaScript配列結合としてのスプレッド(push より読みやすく)
複数の配列を「並べて1つにする」
従来は arr1.concat(arr2) のように書きましたが、スプレッドなら配列リテラルの中で「順番通りに並べる」だけです。ここが重要です:[...a, ...b, ...c] のように書くと、「先に a の中身、そのあと b、そのあと c」という順序がコードから一目でわかります。
const a = [1, 2];
const b = [3, 4];
const merged1 = a.concat(b); // 従来の書き方
const merged2 = [...a, ...b]; // スプレッド構文
console.log(merged2); // [1, 2, 3, 4]
// 真ん中に別の値を挟む
const merged3 = [...a, 99, ...b];
console.log(merged3); // [1, 2, 99, 3, 4]
JavaScript先頭・末尾に要素を足した新配列を作る
push や unshift は元の配列を直接書き換えます。ここが重要です:[新しい要素, ...元] や [...元, 新しい要素] と書けば、「元配列を変更せず、新しい配列を返す」ことができ、バグを減らせます。
const arr = [2, 3];
const withHead = [1, ...arr]; // 先頭に追加
const withTail = [...arr, 4]; // 末尾に追加
console.log(withHead); // [1, 2, 3]
console.log(withTail); // [2, 3, 4]
console.log(arr); // [2, 3] (元はそのまま)
JavaScript関数の引数としての配列展開(apply の代替)
「配列をそのまま引数にしたい」場面
Math.max に「配列全部」を渡したいとき、従来は Math.max.apply(null, arr) のように書いていました。スプレッド構文なら、ここが重要です:Math.max(...arr) で「配列をバラして複数引数にする」ことができます。
const nums = [5, 2, 9];
console.log(Math.max(5, 2, 9)); // 9
console.log(Math.max(...nums)); // 9 (配列を展開して渡す)
JavaScript自作の関数でも同じように使えます。
const sum3 = (a, b, c) => a + b + c;
const arr = [10, 20, 30];
console.log(sum3(...arr)); // 60
JavaScript部分的なスプレッド(固定+可変を混ぜる)
先頭だけ固定で渡し、残りの可変部分はスプレッドで渡す、といった書き方も自然にできます。
const log = (level, ...messages) => {
console.log(level, ...messages);
};
const msgs = ["A", "B", "C"];
log("INFO", ...msgs); // INFO A B C
JavaScript分割代入との違いと組み合わせ(重要な整理)
... が「左側」にあるときと「右側」にあるとき
ここがとても重要なポイントです:
- 分割代入の「左側」での
...restは「残りをまとめて“受け取る”」 - スプレッドの「右側」での
...arrayは「中身をバラして“渡す/並べる”」
同じ ... でも、役割が逆です。
// 左側(分割代入): 残りを“受ける” → rest 要素
const [head, ...tail] = [1, 2, 3]; // head=1, tail=[2, 3]
// 右側(スプレッド): 中身を“渡す/並べる”
const arr = [2, 3];
const withHead = [1, ...arr]; // [1, 2, 3]
JavaScript分割代入とスプレッドを組み合わせた実用例
例えば「最初の1件だけ別扱い、それ以外はまとめて配列にする」処理を、自然な形で書けます。
const rows = [1, 2, 3, 4];
// 先頭だけ取り出す
const [first, ...rest] = rows;
// 別の並びにして新配列を作る
const reordered = [...rest, first]; // [2, 3, 4, 1]
console.log(first); // 1
console.log(reordered); // [2, 3, 4, 1]
JavaScriptよくある落とし穴と注意点(重要ポイントの深掘り)
配列の中身はコピーされるが、オブジェクトは共有(浅いコピー)
[...arr] は新しい配列ですが、中にオブジェクトが入っていると、そのオブジェクト自体は共有されます。書き換えが元にも影響して良いのかを考えて使う必要があります。
const arr = [{ x: 1 }];
const copy = [...arr];
copy[0].x = 99;
console.log(arr[0].x); // 99(共有される)
JavaScript完全に独立させたい場合は、要素ごとにコピーするロジック(map で展開してオブジェクトをコピーするなど)が必要です。
展開先が「配列・引数以外」のとき
スプレッドは配列だけでなく「反復可能なもの(文字列など)」にも使えますが、初心者のうちはまず「配列専用の便利構文」として意識しておくと混乱しにくいです。
console.log([..."abc"]); // ["a", "b", "c"]
JavaScript例題で理解を固める
// 1) ページング結果のマージ:前の結果 + 新しい結果
const prev = [1, 2, 3];
const next = [4, 5];
const merged = [...prev, ...next];
console.log(merged); // [1, 2, 3, 4, 5]
// 2) フィルタ後、先頭に「すべて」オプションを追加
const categories = ["家電", "本", "食品"];
const options = ["すべて", ...categories];
console.log(options); // ["すべて", "家電", "本", "食品"]
// 3) 最大値・最小値の計算(Math.max/Math.min と組み合わせ)
const scores = [72, 88, 95, 64];
const max = Math.max(...scores);
const min = Math.min(...scores);
console.log(max, min); // 95 64
// 4) 配列をコピーしつつ末尾に要素追加(元を変えない)
const base = [10, 20];
const extended = [...base, 30];
console.log(base); // [10, 20]
console.log(extended); // [10, 20, 30]
// 5) 分割代入との組み合わせ:先頭を移動して末尾へ
const queue = [1, 2, 3, 4];
const [first, ...rest] = queue;
const rotated = [...rest, first];
console.log(rotated); // [2, 3, 4, 1]
JavaScriptまとめ
スプレッド構文の配列展開の核心は「配列をバラして、その場に要素を並べる」ことです。[...arr] でコピー、[...a, ...b] で結合、fn(...arr) で「配列を引数リストとして渡す」。右側の ... は「渡す/並べる」、左側の ...(分割代入)は「残りを受ける」と意味が逆になる点をしっかり区別してください。元配列を壊さずに新配列を作る習慣を身につけると、バグが劇的に減り、ES6+ らしいきれいなコードが書けるようになります。

