【実務・中級編】 String.prototype.replaceAll()の活用 – JavaScript実践ガイド

お疲れ。最近、コードレビューをしていて「あ、ここ昔のクセで `replace()` と正規表現のグローバルフラグ (`/g`) 使っちゃってるな」って思うシーンにやたらと遭遇するんだよね。

君も経験ないか? ユーザーが入力したカンマ区切りの数値を一括置換しようとして、`str.replace(‘,’, ”)` と書いたら最初の1個しか消えなくて「あれ?」ってなり、慌てて `str.replace(/,/g, ”)` に書き換える――。

JavaScriptの歴史を知っている古参のエンジニアほど、この「グローバル正規表現ハック」が染みついているはずだ。だが、もう令和のモダンなフロントエンド開発だ。その泥臭いハックは、今日ここで卒業しよう。

今回は、ES2021で導入された救世主、`String.prototype.replaceAll()` について、ブラウザの裏側の動きや `replace()` との決定的な違い、そして実務でどう使い分けるべきかを徹底的に解説してやろう。

—

なぜ `replaceAll` が必要だったのか?:歴史的背景とモヤモヤ

まず、これまでの `.replace()` の仕様を振り返ってみよう。
`replace()` に第一引数として「文字列」を渡した場合、最初に見つかった1箇所しか置換してくれないという、実務では「いや、全部変えてほしいんだけど……」とツッコミを入れたくなる仕様になっていた。

const text = “apple banana apple cherry apple”;

// 第一引数に文字列を渡すと、最初の一致しか置換されない
console.log(text.replace(“apple”, “orange”));
// 出力: “orange banana apple cherry apple” (あれっ、後ろが残ってる!)

これをすべて置換するためには、わざわざ正規表現オブジェクトを作ってグローバルフラグ (`g`) を付与しなきゃいけなかった。

// 正規表現のグローバルフラグを使うしかなかった
console.log(text.replace(/apple/g, “orange”));
// 出力: “orange banana orange cherry orange”

ここで問題になるのが、「ユーザーからの入力値(動的な文字列)を置換したい場合」だ。例えば、検索フォームに入力されたキーワードに含まれる特殊文字(`.` や “ など)をエスケープせずに正規表現に突っ込むと、バグるか予期せぬ挙動を引き起こす。
「ただ特定の文字列を全部別の文字列に置き換えたいだけなのに、なんで正規表現のメタ文字のエスケープ処理まで自前で書かなきゃいけないんだ?」――これが、長年フロントエンドエンジニアが抱えていたフラストレーションだった。

そこで登場したのが `replaceAll()` だ。こいつは、第一引数に普通の文字列を取った場合でも、一致するすべての箇所を容赦なく一括置換してくれる。

—

`replace` vs `replaceAll`:仕様と裏側の挙動の違い

では、実務でコードを書く上で、この2つをどう使い分けるべきか。仕様の差を整理しておこう。

| メソッド | 第一引数に「文字列」を渡した場合 | 第一引数に「正規表現」を渡した場合 |
| :— | :— | :— |
| `replace()` | 最初の一致のみ 置換する | マッチした箇所を置換する(`/g` があればすべて) |
| `replaceAll()` | すべての一致 を置換する | 要注意:`/g` フラグが必須(ないとエラー) |

ここで「おっ?」と思った鋭い読者なら大正解だ。
実は `replaceAll()` の第一引数に正規表現を渡すこともできる。ただし、その場合は グローバルフラグ (`g`) が必須 になる。もし `/g` なしの正規表現を渡すと、JavaScriptエンジン(V8など)は親切にTypeErrorを投げて教えてくれる。

const text = “foo bar foo”;

// エラーになる例:replaceAllで正規表現を使うのに /g がない
try {
text.replaceAll(/foo/, “baz”);
} catch (e) {
console.error(e.message);
// TypeError: String.prototype.replaceAll called with a non-global RegExp argument
}

ブラウザが裏側でどう処理しているか?

V8などのモダンなJavaScriptエンジンは、文字列の検索・置換において非常に高度な最適化を行っている。
内部的には、`replaceAll()` も `replace(/g)` も、C++レベルの高速な文字列スキャンアルゴリズム(Boyer-Moore法やSIMDを活用したパターンマッチングなど)に落ちていく。

したがって、「パフォーマンス上の違い」を気にする必要はほとんどない。
選定基準はただ一つ。「正規表現のパワー(前方一致、文字クラス、キャプチャグループなど)が必要か、それとも純粋な文字列の完全一致で十分か」、これだけだ。

—

現場で即コピペできる!実用的なユースケースとサンプルコード

百聞は一見に如かず。実務のフロントエンド開発でよく遭遇するシーンを例に、綺麗なコードを見ていこう。

1. APIから取得したHTMLやMarkdown風テキストの全置換

例えば、バックエンドから返ってきたテキスト内の改行コードや、プレースホルダー(変数部分)を一括変換するケースだ。正規表現の特殊文字エスケープを気にする必要がないため、コードが劇的にクリーンになる。

/

  • テンプレート文字列内のプレースホルダーをすべて一括置換する関数
  • @param {string} template – 変換前のテンプレート文字列
  • @param {string} target – 置き換えたいキーワード
  • @param {string} replacement – 新しい文字列

/
function applyTemplate(template, target, replacement) {
// 文字列の完全一致ですべて置換するため replaceAll が最適
return template.replaceAll(target, replacement);
}

const rawText = “USER_NAME様、こんにちは。本日の売上は USER_NAME 様の口座に振り込まれました。”;
const formattedText = applyTemplate(rawText, “USER_NAME”, “山田太郎”);

console.log(formattedText);
// 出力: “山田太郎様、こんにちは。本日の売上は 山田太郎 様の口座に振り込まれました。”

2. 金額のカンマ一括削除(ユーザー入力のサニタイズ)

フォームでユーザーが入力したカンマ区切りの数値を、計算用にクリーンアップする処理だ。ここでも `replaceAll` は大いに活躍する。

/

  • 入力された金額文字列からカンマをすべて取り除く
  • @param {string} priceInput – 例: “1,234,567”
  • @returns {number} – 数値型に変換された値

/
function parsePrice(priceInput) {
if (typeof priceInput !== “string”) return 0;

// カンマを空文字にすべて置換
const sanitized = priceInput.replaceAll(“,”, “”);

const parsed = Number(sanitized);
return Number.isNaN(parsed) ? 0 : parsed;
}

console.log(parsePrice(“12,345,678”)); // 出力: 1234567

—

チーフアーキテクトからの実践的なアドバイス(ベストプラクティス)

最後に、現場でレビューをするときに僕が後輩によく言う注意点をいくつかシェアしておこう。

1. 環境要件(ターゲットブラウザ)の確認
`replaceAll()` は比較的新しい仕様(ES2021)だ。Babelなどのトランスパイラを通している環境なら問題ないが、もし古いIE(もうサポート対象外とは思うが……)や、かなり古い組み込み環境をターゲットにしている場合はポリフィルが必要になる。ViteやNext.jsなどのモダンなビルドツールを使っていれば基本は気にしなくていい。
2. 「とりあえず `replace(/g)`」の癖を直す
動的な文字列を置換するときに、うっかり `new RegExp(keyword, ‘g’)` のようなコードを書いていないか? メタ文字(`.` や `?` など)が含まれていた場合にバグの温床になる。純粋な文字列置換なら、迷わず `replaceAll()` を選ぼう。
3. 複雑な条件なら正規表現+`replace` を選ぶ勇気
「大文字小文字を区別せずにすべて置換したい」「数字のパターンにマッチさせたい」といった文脈であれば、`replaceAll` ではなく、これまで通り正規表現(`i` フラグや文字クラス)を使った `replace()` が正解だ。道具の適材適所を忘れないこと。

技術は日進月歩だ。昔の常識に縛られず、より安全で読みやすいモダンなAPIを積極的にプロダクトコードに取り入れていこう。君の書くコードが、次のチームのスタンダードになるんだからな。

コメント

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