やあ。最近、コードレビューをしていて、ふと目についたんだ。
君が書いたその文字列結合のコード、何気なく `concat()` メソッドを使っていないかい?
「複数の文字列を繋げるんだから、専用の `String.prototype.concat()` が一番正しいやり方なんじゃないですか?」
うん、教科書的な綺麗事を言えばその通りだ。メソッド名もそのものズバリだしね。だがね、現場の第一線で数百万人のユーザーが触るモダンなWebアプリケーションを最適化してきたシニアの視点から言わせてもらうと、実務で `concat()` を使う理由は、ほぼゼロに近い。
今回は、この一見無害に見える `concat()` の正体と、ブラウザの裏側で何が起きているのか、そして私たちが日々の開発で選ぶべき「真のベストプラクティス」について、徹底的に解剖していこう。
—
1. `String.prototype.concat()` とは一体何者なのか?
まずは基本の確認だ。`concat()` は、呼び出した文字列に、引数として渡した複数の文字列を連結した「新しい文字列」を返すメソッドだ。
const str1 = “Hello”;
const str2 = “World”;
// 伝統的な concat の使い方
const result = str1.concat(“, “, str2, “!”);
console.log(result); // “Hello, World!”
一見、何の問題もない。メソッドチェーンもできるし、複数の引数を取れるからスッキリ書けるように見える。
しかし、ここにJavaScriptの歴史的な「お荷物」が隠されている。このメソッド、実はJavaの `String.concat()` に影響されてECMAScriptに持ち込まれたものなんだが、JavaScriptの文脈では非常に厄介な特性を持っている。
最大の罠:falsyな値の強制型変換
君は、もし引数に `null` や `undefined`、あるいは数値を渡したらどうなるか知っているかい?
const base = “User: “;
// ユーザーIDが万が一 undefined だった場合
const userId = undefined;
// concat を使うとどうなるか?
console.log(base.concat(userId)); // “User: undefined”
……ゾッとしなかったかい? エラーを吐かずに、親切心(のつもり)で勝手に `”undefined”` という文字列に変換して結合してしまうんだ。バグの温床としてこれ以上のものはない。現代のフロントエンド開発において、型安全や予期せぬ `undefined` の混入を防ぎたい我々にとって、この暗黙の型変換は恐怖でしかないはずだ。
—
2. パフォーマンスの裏側:`concat()` vs 算術演算子 `+` vs テンプレートリテラル
さて、ここからが本題だ。パフォーマンスの話をしよう。
「メソッド呼び出しのオーバーヘッド」と「V8エンジン(ChromeなどのJSエンジン)の最適化」の観点から、これら3つのアプローチを比較してみる。
結論から言うと、`concat()` は最下位だ
ブラウザのJavaScriptエンジンは、文字列の結合処理において凄まじい最適化を行っている。特にV8のようなモダンなエンジンは、コードのコンパイル時に文字列の結合をインライン展開したり、不要なメモリ割り当てを削ったりする。
しかし、`concat()` は関数呼び出し(Function Call)を伴う。これが余計なスタックフレームを生み出し、単純な演算子に比べて数段パフォーマンスが落ちる。ベンチマークを取れば一目瞭然だが、数千回・数万回とループの中で回すと、明らかな速度差として現れる。
では、古くから使われている `+` 演算子はどうだろう?
// 算術演算子(+)による結合
const name = “Taro”;
const greeting = “Hello, ” + name + “!”;
これは悪くない。エンジンも最適化しやすい。だが、可読性が最悪だ。変数が多くなると、どこにシングルクォーテーションを閉じてプラスを挟んだのか、まるで迷路のようになる。
覇者:テンプレートリテラル(ES2015+)
ここで登場するのが、我らがテンプレートリテラルだ。バックティック(“ ` “)で囲むやつだな。
// テンプレートリテラルによる結合(これがベスト)
const name = “Taro”;
const greeting = `Hello, ${name}!`;
可読性は圧倒的。改行もそのまま書ける。そして何より、JavaScriptエンジン側で極限まで最適化されるように設計されている。内部的には、複数の文字列ピースを効率的に配列としてまとめ、最後に一気にメモリを確保して結合する処理(コンパイル時の最適化)が働くため、パフォーマンス的にも `concat()` を完全に凌駕している。
—
3. 実務で直面するシナリオと実践的なコード例
「理屈はわかったけど、実際の現場ではどう書き分ければいいんだ?」という君のために、今日からそのまま使えるベストプラクティスをコードスニペットとして残しておこう。
パターンA: 動的なリスト(配列)からの結合
もし、APIから返ってきた文字列の配列を結合するような場合は、`concat()` ではなく `Array.prototype.join()` を使うのが鉄則だ。
/
- 現場でよくある例:タグの配列をカンマ区切りの文字列に変換する
/
const tags = [“JavaScript”, “TypeScript”, “React”, “Architecture”];
// NG: 愚直に concat をループさせる(メモリ効率最悪)
// let result = “”;
// tags.forEach(tag => { result = result.concat(“, “, tag); });
// GOOD: join を使う。パフォーマンスもコードの美しさも段違い。
const formattedTags = tags.join(“, “);
console.log(formattedTags); // “JavaScript, TypeScript, React, Architecture”
パターンB: 条件付きの複雑な文字列構築
「特定の条件のときだけ、文字列の一部を追加したい」というケースもあるだろう。テンプレートリテラルと三項演算子、あるいは短絡評価を組み合わせるのがスマートだ。
/
- 現場でよくある例:権限に応じたウェルカムメッセージの生成
/
function createWelcomeMessage(username, isAdmin) {
// テンプレートリテラルの中で式を評価する
return `ようこそ、${username}さん!${isAdmin ? “【管理者権限】” : “”}`;
}
console.log(createWelcomeMessage(“Yamada”, true));
// 出力: “ようこそ、Yamadaさん!【管理者権限】”
console.log(createWelcomeMessage(“Tanaka”, false));
// 出力: “ようこそ、Tanakaさん!”
※もしここで `concat()` を使おうものなら、`isAdmin` が `false` のときに `”false”` という文字列が結合される大惨事になっていたところだ。危ない危ない。
—
まとめ:シニアからのメッセージ
今日の話をまとめておこう。
1. `String.prototype.concat()` は封印せよ。
理由:関数呼び出しのオーバーヘッドがあり、`null` や `undefined` を暗黙的に文字列化してしまう危険な仕様だから。
2. 基本は「テンプレートリテラル(“ `…${}` “)」を使え。
理由:可読性が高く、モダンなJSエンジンによって強力に最適化されるため。
3. 配列の結合には `Array.prototype.join()` を選べ。
理由:ループで繋ぐよりも圧倒的にスマートで高速だから。
技術の歴史が長いJavaScriptには、こうした「過去の遺物」のようなメソッドがまだいくつか眠っている。公式ドキュメントに載っているからといって、思考停止で使うのは今日で終わりにしよう。
裏側でエンジンがどう動いているか、メモリやパフォーマンスにどう影響するか——そこまで想像力を働かせることこそが、中級から「真のシニア・フロントエンドエンジニア」へステップアップするための第一歩だ。
さて、君のプロジェクトにある古いコード、今すぐリファクタリングしに行こうか。

コメント