JavaScriptにおけるデータの操作は、アプリケーション開発において避けては通れない非常に重要な要素です。
特に複数の配列や文字列を結合する処理は、フロントエンドからサーバーサイドまで幅広いシーンで頻繁に利用されます。
データの不変性(イミュータビリティ)を保ちながら効率的に結合を行うための手法として、古くから親しまれているのがconcatメソッドです。
近年ではスプレッド構文などの新しい記法も一般的になっていますが、concatメソッド独自の特性やメリットを理解しておくことは、高品質なコードを書く上で欠かせません。
本記事では、concatメソッドの基本的な使い方から、最新の開発現場で求められるスプレッド構文との使い分けまで、詳しく解説していきます。
JavaScriptのconcatメソッドとは
concatメソッドは、2つ以上の配列や文字列を結合し、新しいインスタンスを生成するためのメソッドです。
「concatenate(連結する)」という英単語が名前の由来となっており、その名の通り複数の要素を一つにまとめる役割を担います。
JavaScriptにおいて、配列(Array)と文字列(String)の両方にこのメソッドが用意されていますが、一般的には配列の操作で使われることが圧倒的に多い傾向にあります。
concatメソッドの最大の特徴は、元の配列や文字列を変更しない「非破壊的」なメソッドであるという点です。
現代のプログラミング、特にReactやVue.jsといったモダンなフレームワークを利用した開発では、元のデータを直接書き換えない「不変性」が重視されます。
そのため、既存のデータに影響を与えずに新しいデータを作成できるconcatは、非常に相性の良いメソッドと言えます。
配列におけるconcatの役割
配列におけるconcatは、指定した配列に対して他の配列や値を結合し、その結果を新しい配列として返します。
引数には配列だけでなく、数値や文字列といった個別の値を直接渡すことも可能です。
この柔軟性により、複雑なデータ構造の構築をシンプルに記述することができます。
文字列におけるconcatの役割
文字列におけるconcatも同様に、ある文字列に別の文字列を連結して新しい文字列を生成します。
しかし、文字列の結合に関しては、プラス演算子(+)やテンプレートリテラル(`${}`)が主流となっています。
そのため、メソッド形式で呼び出すconcatは、特定のコーディング規約がある場合や、関数型プログラミングの文脈以外では見かける機会が少なくなっています。
配列を結合する基本操作
配列の結合において、concatメソッドを使用する方法は非常に直感的です。
まず、ベースとなる配列を定義し、その後に.concat()を呼び出し、引数に結合したい要素を指定します。
具体的なコード例を見ていきましょう。
// ベースとなる配列
const array1 = ['りんご', 'バナナ'];
const array2 = ['オレンジ', 'ぶどう'];
// 配列を結合する
const combinedArray = array1.concat(array2);
console.log('結合後の配列:', combinedArray);
console.log('元の配列1:', array1); // 元の配列は変更されない
結合後の配列: ["りんご", "バナナ", "オレンジ", "ぶどう"]
元の配列1: ["りんご", "バナナ"]
実行結果からわかるように、array1の内容は書き換わっていません。
このように、元のデータを保護しながら新しい配列を得られるのが大きなメリットです。
複数の配列を一度に結合する
concatメソッドには、複数の引数を渡すことができます。
3つ以上の配列を一度にまとめたい場合でも、メソッドチェーンを繰り返す必要はありません。
const list1 = [1, 2];
const list2 = [3, 4];
const list3 = [5, 6];
const allNumbers = list1.concat(list2, list3);
console.log(allNumbers);
[1, 2, 3, 4, 5, 6]
このように、カンマ区切りで複数の配列を指定するだけで、それらが順番に連結されます。
配列と値を混ぜて結合する
concatの引数には、配列以外のプリミティブな値も指定可能です。
配列の中に個別の要素を追加したい場合にも便利です。
const initialItems = ['ペン', 'ノート'];
const updatedItems = initialItems.concat('消しゴム', ['定規', 'コンパス']);
console.log(updatedItems);
["ペン", "ノート", "消しゴム", "定規", "コンパス"]
引数が配列の場合はその要素が展開されて結合され、値の場合はそのまま要素として追加されます。
文字列を結合する基本操作
文字列におけるconcatメソッドの使い方も配列とほぼ同じです。
しかし、配列の時とは異なり、いくつか注意すべき点があります。
const greeting = 'こんにちは、';
const userName = '田中さん';
const message = greeting.concat(userName, '。今日は良い天気ですね。');
console.log(message);
こんにちは、田中さん。今日は良い天気ですね。
文字列のconcatも元の文字列を変更せず、新しい文字列を生成して返します。
演算子やテンプレートリテラルとの違い
現代のJavaScript開発では、文字列の結合にconcatを使うことは推奨されないケースが多いです。
その理由は、テンプレートリテラルの方が可読性が高く、演算子の方がパフォーマンスに優れる場合があるからです。
// テンプレートリテラル(推奨)
const name = '佐藤';
const welcome = `ようこそ、${name}さん!`;
// プラス演算子
const welcome2 = 'ようこそ、' + name + 'さん!';
特別な理由がない限りはテンプレートリテラルを使用し、concatは配列操作に限定して利用するのが一般的です。
スプレッド構文(…)との使い分け
ES6から導入された「スプレッド構文」は、concatメソッドに代わる配列結合の手段として広く普及しました。
現在では、多くのエンジニアがスプレッド構文を好んで使用しています。
ここでは、concatとスプレッド構文の違いを整理し、どのように使い分けるべきかを解説します。
スプレッド構文による結合
スプレッド構文は、配列の前に...を付けることで、その要素を展開する記法です。
const arrA = [10, 20];
const arrB = [30, 40];
const combined = [...arrA, ...arrB];
console.log(combined);
[10, 20, 30, 40]
スプレッド構文の利点は、記述が簡潔であり、結合する位置を自由にコントロールできる点にあります。
concatとスプレッド構文の比較表
それぞれの特徴を比較表にまとめました。
| 特徴 | concatメソッド | スプレッド構文(…) |
|---|---|---|
| 記法の形式 | メソッド形式(a.concat(b)) | リテラル形式([...a, ...b]) |
| 非破壊性 | あり(新しい配列を生成) | あり(新しい配列を生成) |
| 対応ブラウザ | 非常に古いブラウザでも動作 | モダンブラウザのみ(トランスパイルが必要な場合あり) |
| 柔軟性 | 引数にnullやundefinedが混ざってもエラーになりにくい | 反復可能オブジェクト(Iterable)でないとエラーになる |
| 可読性 | 関数型プログラミングに馴染みやすい | 直感的で視覚的にわかりやすい |
どちらを使うべきか
結論から述べると、基本的にはスプレッド構文を推奨します。
ソースコードが短くなり、配列のどの位置にどの要素を挿入するかが一目でわかるためです。
一方で、大規模なデータの処理や、特定の安全性が必要な場合にはconcatが適していることもあります。
concatが有利なケース:安全な結合
スプレッド構文は、展開しようとする値がnullやundefinedの場合、エラー(TypeError)をスローします。
一方で、concatは引数が配列でない場合でも単なる要素として扱うため、エラーが発生しにくいという特性があります。
const base = [1, 2];
const unexpectedValue = null;
// スプレッド構文だとエラーになる可能性がある(状況による)
// const failed = [...base, ...unexpectedValue]; // エラー
// concatならエラーにならずに処理できる
const safe = base.concat(unexpectedValue);
console.log(safe);
[1, 2, null]
このように、外部APIなどから取得した「型が不確実なデータ」を扱う際には、concatの方が堅牢なコードになる場合があります。
パフォーマンスと大規模データの処理
小規模な配列であればどちらの手法を使っても速度差は体感できません。
しかし、数万件、数十万件といった膨大なデータを扱う場合、パフォーマンスの違いが顕著に現れることがあります。
メモリ使用量とコピーのコスト
concatもスプレッド構文も、内部的には「新しい配列のメモリ確保」と「要素のコピー」を行っています。
そのため、非常に大きな配列を頻繁に結合すると、メモリ不足やガベージコレクションの頻発を招く恐れがあります。
大規模データを扱う際は、新しい配列を作らずに既存の配列へ要素を追加するpush.applyやpush(スプレッド展開)の検討が必要です。
const target = [1, 2, 3];
const source = [4, 5, 6];
// 破壊的だがメモリ効率が良い方法
target.push(...source);
console.log(target);
ただし、この方法は「元の配列を書き換える」ため、不変性が求められる文脈では使用できません。
要件に合わせて、安全性とパフォーマンスのバランスを取ることが重要です。
シャローコピー(浅いコピー)の注意点
concatを使用する上で絶対に忘れてはならないのが、「シャローコピー(浅いコピー)」であるという性質です。
これは、配列の中にオブジェクトが含まれている場合、そのオブジェクトの実体まではコピーされないことを意味します。
const obj = { name: 'テスト' };
const array1 = [obj];
const array2 = ['追加データ'];
const result = array1.concat(array2);
// 結合後の配列内のオブジェクトを変更する
result[0].name = '書き換え完了';
// 元の配列の中身も変わってしまう
console.log(array1[0].name);
書き換え完了
このように、結合後の配列にあるオブジェクトを操作すると、元の配列内のオブジェクトも連動して変化します。
これはコピーされたのが「オブジェクトへの参照(住所情報)」だけであり、オブジェクトそのものではないためです。
完全なコピー(ディープコピー)が必要な場合は、structuredClone()などの別の手段を併用する必要があります。
応用的な活用:配列のフラット化
古くは、concatを利用して2次元配列を1次元に平坦化(フラット化)する手法がよく使われていました。
現在ではArray.prototype.flat()が標準化されているため、その役割は譲っていますが、知識として知っておくと古いコードの解読に役立ちます。
const nestedArray = [[1, 2], [3, 4], [5, 6]];
// concatを使ったフラット化の例(古い手法)
const flattened = [].concat.apply([], nestedArray);
console.log(flattened);
[1, 2, 3, 4, 5, 6]
モダンな環境であれば、迷わず nestedArray.flat() を選択しましょう。
歴史的な背景を知ることで、なぜconcatが今のような形になっているのかをより深く理解できます。
まとめ
JavaScriptのconcatメソッドは、配列や文字列を安全かつシンプルに結合するための基本的なツールです。
元のデータを破壊せずに新しいデータを生成する特性は、予測可能性の高いプログラミングを実現するために非常に有効です。
文字列の結合についてはテンプレートリテラルに主役を譲っていますが、配列操作においては現在も有力な選択肢の一つです。
特に、スプレッド構文との違いを正しく理解し、安全性を重視する場面ではconcat、記述の簡潔さを求める場面ではスプレッド構文、といった使い分けができるようになりましょう。
また、大規模データを扱う際にはシャローコピーの性質やパフォーマンスへの影響を考慮することも忘れないでください。
これらの知識を身につけることで、状況に応じた最適な配列操作を選択できるようになり、より洗練されたコードが書けるようになるはずです。
