【初学者向け】配列やオブジェクトを簡単に複製できる!スプレッド演算子について解説!

JavaScript

はじめに

ES6で追加された機能の一つにスプレッド演算子があります。スプレッド演算子は、可読性やコードの簡潔さを向上させることができ、配列やオブジェクトを操作する際に非常に便利です。

ES5以前では、配列を結合したり配列のコピーを作成する際にはconcat()メソッドを使う必要がありました。また、オブジェクトをマージする場合には手動でプロパティを複製する必要がありました。スプレッド演算子を使用することで、これらの操作を簡潔に表現することができます。

この記事では、スプレッド演算子の基本的な使い方について、初学者の方がスプレッド演算子のメリットを理解しやすいようにES5以前の方法との比較を交えて解説します。

この記事の対象者

  • JavaScript初学者の方
  • 「…」が分からない方
  • 可読性の高いコードを意識したい方

この記事で学べること

  • スプレッド演算子の役割
  • スプレッド演算子の基本的な使い方

スプレッド演算子の使い方

スプレッド演算子は、配列やオブジェクトを展開してその中身を取り出すための機能です。スプレッド演算子の役割は「配列の展開」であり、これを使用することで配列やオブジェクトの複製や結合を可読性の高い表現で行うことができます。

スプレッド演算子の基本的な使い方について、配列とオブジェクト、関数の引数としての利用を焦点に当てて解説します。

配列でのスプレッド演算子

既存の配列を複製して新しい配列を作成したり、複数の配列を結合したりといった処理を行う際、ES5以前ではconcat()メソッドがよく利用されていました。

concat()メソッドは使い慣れていれば特に難しく感じることはありません。しかし、concat()メソッドは汎用的に利用できるという特徴により、配列の複製や配列の結合、要素の追加といった配列の操作以外にも、文字列型のデータにも使用されることがあります。状況や開発者の好みなどによって多くの場面で使われることがあるため、引数に指定する値が分かりづらく直感的ではない表現になる場合があります。

スプレッド演算子を使用することで、配列の操作をより簡潔に行うことができます。concat()メソッドよりも直感的な表現になり、配列の複製や結合など、さまざまな場面で活用することができます。

配列の複製

ES5以前の方法で配列を複製して新しい配列を作成するには、concat()メソッドを以下のように使用します。

// ES5以前
var originalArray = [1, 2, 3];
var copiedArray = originalArray.concat(); // [1, 2, 3]

スプレッド演算子では、配列の複製を簡潔な表現で行うことができます。

// スプレッド構文
const originalArray = [1, 2, 3];
const copiedArray = [...originalArray]; // [1, 2, 3]

上記のように、「 […変数名] 」と記述することで変数に格納された配列を新しい配列に展開することができます。

また、スプレッド演算子で複製された配列は非破壊的メソッドであるconcat()によるコピーと同様、元の配列とは別のものとして扱われます。よって、元の配列の変更が複製された配列に影響を与えることはなく、その逆もまた然りです。

const originalArray = [1, 2, 3];
const copiedArray = [...originalArray];

// 以下を追加
originalArray[3] = 'originalArrayItem';
copiedArray[3] = 'copiedArrayItem';

// 各配列は別のものとして扱われます
console.log(originalArray);
// 結果:[1, 2, 3, 'originalArrayItem']

console.log(copiedArray);
// 結果:[1, 2, 3, 'copiedArrayItem']

配列の結合

ES5以前の方法で複数の配列を結合して新しい配列を作成するには、concat()メソッドを以下のように使用します。

// ES5以前
var numbers1 = [1, 2, 3];
var numbers2 = [4, 5, 6];
var combinedNumbers = numbers1.concat(numbers2);
// [1, 2, 3, 4, 5, 6]

スプレッド演算子では、配列の結合を以下のように行います。

// スプレッド構文
const numbers1 = [1, 2, 3];
const numbers2 = [4, 5, 6];
const combinedNumbers = [...numbers1, ...numbers2];
// [1, 2, 3, 4, 5, 6]

このように、スプレッド演算子を使用することによって配列の中で他の配列を展開することができ、配列のコピーや結合を直感的な表現で行うことができます。

オブジェクトでのスプレッド演算子

ES5以前、オブジェクトの完全なコピーを作成するためにはループを回して各プロパティを複製する必要がありました。オブジェクト同士をマージする際も同様で、複数のオブジェクトの各プロパティを取得して新しいオブジェクトに設定するための処理を記述しなければいけませんでした。

ES6から導入されたスプレッド演算子を使用することで、この作業を簡潔に行うことができます。

オブジェクトのコピー

オブジェクトの複製の方法について、以下のように行うのが最も簡単だと考える方がいるかもしれません。

const originalObject = { x: 1, y: 2 };
const copiedObject = originalObject;

一見すると問題がないように思うかもしれませんが、上記の例のcopiedObjectはoriginalObjectを複製しているのではなく参照を行っているだけです。よって、双方のプロパティの変更はお互いに影響を与えます。

const originalObject = { x: 1, y: 2 };
const copiedObject = originalObject;

originalObject.z = 3;
copiedObject.x = 4;

// 両方同じ値になります
console.log(originalObject);
// 結果:{x: 4, y: 2, z: 3}
console.log(copiedObject);
// 結果:{x: 4, y: 2, z: 3}

ES5以前での方法でオブジェクトの複製を作成するには、以下のように行う必要がありました。

// オブジェクトを複製する関数を定義
function copy(obj) {
  var newObj = {};
  
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] = obj[key];
    }
  }
  
  return newObj;
} 

const originalObject = { x: 1, y: 2 };

// copy関数を使用してoriginalObjectをコピー
const copiedObject = copy(originalObject);

// 複製されたことを確認します
console.log(copiedObject);
// 結果:{x: 1, y: 2}

// 違うオブジェクトとして扱われることを確認します
originalObject.z = 3;
copiedObject.x = 4;

console.log(originalObject);
// 結果:{x: 1, y: 2, z: 3}
console.log(copiedObject);
// 結果:{x: 4, y: 2}

複製されたオブジェクトが複製元の参照ではなく別のオブジェクトとして扱われていることが確認できます。しかし、オブジェクトを複製するだけの処理でも記述量が多くてあまり直感的とは言えません。

スプレッド演算子を使用することで既存のオブジェクトを簡単に複製することができます。配列の複製と同様に、元のオブジェクトを変更しても複製されたオブジェクトに影響を与えることがありません。

// スプレッド演算子
const originalObject = { x: 1, y: 2 };
const copiedObject = { ...originalObject };

// 複製されたことを確認します
console.log(copiedObject);
// 結果:{x: 1, y: 2}

// 違うオブジェクトとして扱われることを確認します
originalObject.z = 3;
copiedObject.x = 4;

console.log(originalObject);
// 結果:{x: 1, y: 2, z: 3}
console.log(copiedObject);
// 結果:{x: 4, y: 2}

オブジェクトのマージ

ES5以前の方法でオブジェクトのマージを行う際、複製と同様に関数を定義して実現する必要があります。

// オブジェクトをマージする関数
function mergeObjects(obj1, obj2) {
  var mergedObj = {};
    
  for (var key in obj1) {
    if (obj1.hasOwnProperty(key)) {
       mergedObj[key] = obj1[key];
    }
  }
    
  for (var key in obj2) {
    if (obj2.hasOwnProperty(key)) {
      mergedObj[key] = obj2[key];
    }
  }
    
  return mergedObj;
}

var obj1 = { a: 1, b: 2 };
var obj2 = { c: 3, d: 4 };
// mergeObjects関数を使用します
var mergedObject = mergeObjects(obj1, obj2);

console.log(mergedObject);
// 結果:{ a: 1, b: 2, c: 3, d: 4 }

上記のmergeObjects()関数は、解説を簡単にするために二つのオブジェクトしかマージできない仕様です。3つ以上のオブジェクトに対応するには可変長引数で定義する必要があり、さらに工夫が必要になります。

スプレッド演算子を使用することで、既存のオブジェクトを展開し、新しいオブジェクトにマージすることができます。3つ以上のオブジェクトのマージも簡単に行うことができます。

const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const obj3 = { e: 5, f: 6 };

// 3つのオブジェクトをマージします
const mergedObject = { ...obj1, ...obj2, ...obj3 };

// 正しくマージされていることを確認します
console.log(mergedObject);
// 結果:{a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}

このように、スプレッド演算子を使用することでオブジェクトの操作を簡潔かつ直感的に行うことができます。ここではオブジェクトの複製とマージに焦点を当てましたが、工夫次第でさまざまな場面で活用することができるでしょう。

関数の引数としての利用

スプレッド演算子は配列やオブジェクトを展開するという性質上、可変長引数を受け入れる関数を利用する際にも使用することができます。この関数は自分で定義することもできますが、JavaScriptで元々定義されているものもあります。

簡単に扱えるものとして、Math.max()とMath.min()を例にしてスプレッド演算子を利用してみます。これらの関数は以下の処理を行います。

  • Math.max():渡された数値の中で最大値を返す
  • Math.min():渡された数値の中で最小値を返す

数値型のデータを複数持つ配列があるとし、この配列の中から最大値と最小値を出力する処理を考えます。ループ処理でも行うことができますが、上記の関数を利用すると簡潔になります。

これらの関数を扱う際の基本として、Math.max()とMath.min()は配列を直接引数として受け取ることはできません。

const numbers = [5, 10, 3, 8];

const maxNumber = Math.max(numbers);
console.log(maxNumber);
// 結果:NaN

const minNumber = Math.min(numbers);
console.log(minNumber);
// 結果:NaN

ES5以前では何らかの方法で配列を展開する必要がありました。以下はapply()メソッドを利用する例です。

var numbers = [5, 10, 3, 8];

// 最大値を求める
var maxNumber = Math.max.apply(null, numbers);
console.log(maxNumber);
// 結果: 10

// 最小値を求める
var minNumber = Math.min.apply(null, numbers);
console.log(minNumber);
// 結果: 3

apply()メソッドを利用することで結果的に配列を展開することができますが、あまり直感的ではありません。スプレッド演算子を使用すると以下のようになります。

const numbers = [5, 10, 3, 8];

// 最大値を求める
const maxNumber = Math.max(...numbers);
console.log(maxNumber);
// 結果:10

// 最小値を求める
const minNumber = Math.min(...numbers);
console.log(minNumber);
// 結果:3

スプレッド演算子が配列を展開する役割を持っていることを理解していれば、上記のような記述で動作する理由が分かるかと思います。

最後に

この記事では、スプレッド演算子の基本的な使い方について解説しました。スプレッド演算子を使用することで、コードをより簡潔かつ効率的に書くことができます。スプレッド演算子は可読性の向上に貢献するため、昨今のJavaScript開発では多くの場面で使用されています。

スプレッド演算子は「スプレッド構文」と表現されることがあります。スプレッド構文はスプレッド演算子よりも広義的な概念であり、その中に「レスト演算子」というものを含みます。

レスト演算子はスプレッド演算子と同様に「…」で表現されますが、「複数のデータを配列にまとめる」という役割を持ち、配列やオブジェクトを展開するスプレッド演算子とは逆の性質を持ちます。

本記事では、スプレッド演算子を関数の引数に利用する例として可変長引数を受け入れるMath.max()関数とMath.min()関数においてのスプレッド演算子の使い方について解説しましたが、レスト演算子の 「複数のデータを配列にまとめる」という特徴を活用することで、可変長引数を受け入れる独自の関数を簡潔に定義することができます。

レスト演算子については以下の記事で詳しく解説しています。興味のある方は参考にしていただければ幸いです。

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