JavaScript | ES6+ 文法:スプレッド構文 – 旧構文との比較

JavaScript
スポンサーリンク

スプレッド構文と「旧構文」をざっくり比較すると

ES6 のスプレッド構文 ... は、
「配列やオブジェクトの中身をその場でバラして並べる」ための新しい書き方です。

旧構文(ES5 まで)は、concat / slice / push / Object.assign / apply などを組み合わせて同じことをしていました。
ここが重要です:

  • できることはほぼ同じでも、スプレッド構文の方が「意図が読みやすい」「バグを生みにくい」
  • 旧構文は「何をしたいのか」を頭の中で変換しないと理解しづらい

まずは、「旧構文でやっていたことを、スプレッド構文でどう書き換えるか」を見ていきます。

配列の結合:concat vs スプレッド

ES5 の書き方(concat)

var a = [1, 2];
var b = [3, 4];

var merged = a.concat(b);
console.log(merged); // [1, 2, 3, 4]
JavaScript

何をしているか分かるものの、a.concat(b) から「a のあとに b をくっつけて新しい配列を作っている」と一瞬変換が必要です。

ES6 の書き方(スプレッド)

const a = [1, 2];
const b = [3, 4];

const merged = [...a, ...b];
console.log(merged); // [1, 2, 3, 4]
JavaScript

[...a, ...b] は、そのまま「a の中身、そのあと b の中身」と読めます。
ここが重要です:「どの配列がどの順番で並ぶか」が見た目そのまま になっていること。

真ん中に値を挟むのも、スプレッドの方が直感的です。

// ES5
var merged = a.concat([99]).concat(b);

// ES6
const merged2 = [...a, 99, ...b];
JavaScript

配列コピー:slice vs スプレッド

ES5 の書き方(slice)

var arr = [1, 2, 3];
var copy = arr.slice(); // 全体コピー

copy[0] = 99;
console.log(arr);  // [1, 2, 3]
console.log(copy); // [99, 2, 3]
JavaScript

slice() 本来の意味は「一部を切り出す」ですが、
「引数なしで呼ぶとコピーになる」という“お約束”を知っていないと読みづらいです。

ES6 の書き方(スプレッド)

const arr = [1, 2, 3];
const copy = [...arr];

copy[0] = 99;
console.log(arr);  // [1, 2, 3]
console.log(copy); // [99, 2, 3]
JavaScript

[...arr] は、“中身をバラして新しい配列に入れ直している”ことがそのまま見えるので、
「これはコピーだな」と直感的に理解できます。

ここが重要です:
スプレッドを覚えておくと、「concat / slice / push などの“裏技”的な使い方」を暗記しなくてよくなります。

関数引数展開:apply vs スプレッド

ES5 の書き方(apply)

配列を「複数の引数」として渡したいとき、 ES5 では apply を使うのが定番でした。

var nums = [5, 2, 9];

var max = Math.max.apply(null, nums);
console.log(max); // 9
JavaScript

apply(null, nums) の意味を知らないと、「なぜ null?」「なぜ配列がここに?」と戸惑います。

ES6 の書き方(スプレッド)

const nums = [5, 2, 9];

const max = Math.max(...nums);
console.log(max); // 9
JavaScript

Math.max(...nums) は、「nums の中身をバラして渡している」と読めます。

自作関数でも同じです。

// ES5
function sum3(a, b, c) {
  return a + b + c;
}
var arr = [10, 20, 30];
console.log(sum3.apply(null, arr));

// ES6
const sum3 = (a, b, c) => a + b + c;
const arr2 = [10, 20, 30];
console.log(sum3(...arr2));
JavaScript

ここが重要です:
「配列をそのまま渡したい」のか、「配列の中身を引数として渡したい」のかが ... の有無だけで明確になる 点です。

オブジェクトのマージ:Object.assign vs スプレッド

ES5 の書き方(Object.assign)

オブジェクトをマージする標準的な方法は Object.assign でした。

var base = { theme: "light", lang: "ja" };
var extra = { lang: "en", debug: true };

var merged = Object.assign({}, base, extra);
console.log(merged);
// { theme: "light", lang: "en", debug: true }
JavaScript

Object.assign({}, base, extra) は、

  • 空オブジェクト {}
  • base の中身をコピーして
  • さらに extra の中身で上書き

を意味しますが、それを一瞬で読み取るには慣れが必要です。

ES6 の書き方(スプレッド)

const base = { theme: "light", lang: "ja" };
const extra = { lang: "en", debug: true };

const merged = { ...base, ...extra };
console.log(merged);
// { theme: "light", lang: "en", debug: true }
JavaScript

{ ...base, ...extra } は、base の中身を書き出して、その後に extra の中身を書き出す、という意味になります。

ここが重要です:

  • Object.assign は「関数呼び出しの知識」が必要
  • スプレッドは「中身を貼り付けている」イメージで、構造から自然に読める

さらに、「一部だけ変えた新オブジェクト」を作るのもスプレッドの方が明快です。

// ES5
var config = { theme: "light", lang: "ja", debug: false };
var newConfig = Object.assign({}, config, { debug: true });

// ES6
const config = { theme: "light", lang: "ja", debug: false };
const newConfig = { ...config, debug: true };
JavaScript

「元を壊さない」書き方の違い:直接変更 vs スプレッド

ES5 の典型的なミスパターン(直接変更)

var config = { theme: "light", lang: "ja" };

function enableDarkTheme(cfg) {
  cfg.theme = "dark"; // 引数を直接書き換え
}

enableDarkTheme(config);
console.log(config.theme); // "dark"(元が変わってしまう)
JavaScript

このようなコードは、「関数を呼び出すと元のオブジェクトが書き換わる」ので、
バグの原因になりがちです。

ES6+スプレッドでの不変スタイル

const config = { theme: "light", lang: "ja" };

function enableDarkTheme(cfg) {
  // 元をいじらず、「新しい設定」を返す
  return { ...cfg, theme: "dark" };
}

const newConfig = enableDarkTheme(config);
console.log(config.theme);    // "light"
console.log(newConfig.theme); // "dark"
JavaScript

ここが重要です:

  • ES5 でももちろん「コピーしてから変更する」ことはできますが、書き方がごちゃつきやすい
  • スプレッド構文を使うと、「元+変更点→新オブジェクト」という不変スタイルを簡潔に書ける

この差が、コードの安全性と読みやすさに直結します。

スプレッド構文を選ぶべき理由(旧構文との本質的な違い)

機能的には、ほとんどのことが ES5 でもできます。
それでもスプレッド構文を使う価値が高い理由は、次のような点に集約されます。

読みやすさ:
「何をしたいか」が構文の形に現れており、関数呼び出しの知識に頼らなくていい。

// 旧:何してるか一瞬考える
Object.assign({}, base, extra);

// 新:見たまま
{ ...base, ...extra }
JavaScript

不変スタイルとの相性:
「元をそのまま」「新しいものを返す」というパターンを自然に書ける。

// 配列
const newArr = [...oldArr, item];

// オブジェクト
const newObj = { ...oldObj, changed: value };
JavaScript

バグの減少:
「コピーしたつもりで同じものを触っていた」などのミスを減らしやすい。

// 参照コピー(危険)
const same = original;

// 浅いコピー(安全性が高い)
const copy = [...original];
const copy2 = { ...original };
JavaScript

まとめると、
ES5 =「知っていればできるけど、意図が隠れやすい」
ES6 スプレッド =「できることは同じでも、意図が見える・安全に書ける」

という違いだと捉えると分かりやすいです。

例題で「旧構文 → スプレッド構文」の変換に慣れる

// 1) 配列結合
// ES5
var a = [1, 2], b = [3, 4];
var mergedOld = a.concat(b);

// ES6
const mergedNew = [...a, ...b];

// 2) 配列コピー
// ES5
var nums = [1, 2, 3];
var copyOld = nums.slice();

// ES6
const copyNew = [...nums];

// 3) 関数引数展開
// ES5
function sum3(a, b, c) { return a + b + c; }
var arr = [10, 20, 30];
var resultOld = sum3.apply(null, arr);

// ES6
const sum3New = (a, b, c) => a + b + c;
const resultNew = sum3New(...arr);

// 4) オブジェクトマージ
// ES5
var baseCfg = { theme: "light" };
var extraCfg = { debug: true };
var mergedCfgOld = Object.assign({}, baseCfg, extraCfg);

// ES6
const mergedCfgNew = { ...baseCfg, ...extraCfg };
JavaScript

まとめ

スプレッド構文は、旧構文(concat / slice / apply / Object.assign など)でやっていたことを、

  • より短く
  • より意図が分かりやすく
  • より「元を壊さない」スタイルで

書けるようにするための ES6 の強力な武器です。

旧構文を「覚え直す」感覚ではなく、
「昔こう書いてたものを、今はこう書き直せる」 と対応づけて覚えると、
スッと頭に入ってきます。

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