JavaScript | ES6+ 文法:スプレッド構文 – 配列展開

JavaScript
スポンサーリンク

スプレッド構文の「配列展開」とは何か

スプレッド構文の配列展開は、...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

先頭・末尾に要素を足した新配列を作る

pushunshift は元の配列を直接書き換えます。ここが重要です:[新しい要素, ...元][...元, 新しい要素] と書けば、「元配列を変更せず、新しい配列を返す」ことができ、バグを減らせます。

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+ らしいきれいなコードが書けるようになります。

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