【完全理解】JavaScript `replaceAll` の使い方、注意点、サンプルコード

はい、承知いたしました。JavaScriptの replaceAll メソッドについて、使い方、注意点、サンプルコードを含む約5000語の詳細な記事を作成します。


【完全理解】JavaScript replaceAll の使い方、注意点、サンプルコード の詳細な説明

JavaScriptの文字列操作において、特定の部分を別の文字列に置き換える処理は非常に頻繁に行われます。これまで、この目的には主に String.prototype.replace() メソッドが使われてきましたが、このメソッドにはいくつかの挙動の癖がありました。特に、文字列を指定して「すべて」置換したい場合や、正規表現で「すべて」置換したい場合に、少し注意が必要でした。

このような背景から、ES2021(ECMAScript 2021)で新しく導入されたのが String.prototype.replaceAll() メソッドです。このメソッドは、文字列中のすべての一致する部分を簡単に、そして明確に置き換えるために設計されました。

この記事では、JavaScriptの replaceAll() メソッドについて、その基本的な使い方から、正規表現を使った高度な置換、置換文字列内の特殊パターン、関数を使った動的な置換、そして重要な注意点まで、完全に理解できるよう詳細に解説します。豊富なサンプルコードと共に学んでいきましょう。

1. なぜ replaceAll が必要なのか? replace との違い

まず、なぜ replaceAll という新しいメソッドが必要とされたのか、既存の replace メソッドと比較しながらその理由を理解しましょう。

String.prototype.replace() メソッドは、第一引数に検索対象(文字列または正規表現)、第二引数に置換後の文字列または関数を指定します。

例えば、「apple」という単語を「orange」に置換したい場合を考えます。

“`javascript
const text = “I have an apple, and you have an apple too.”;

// replaceを使用
const newTextReplace = text.replace(“apple”, “orange”);
console.log(newTextReplace); // 出力: “I have an orange, and you have an apple too.”
“`

この例を見てわかるように、replace メソッドに文字列を指定した場合、最初に見つかった一致のみが置換されます。「apple」は文章中に2回出現していますが、1回目の「apple」だけが「orange」に置き換わっています。

では、すべてを置換したい場合はどうすればよいでしょうか? replace メソッドで全てを置換するには、第一引数にグローバル検索フラグ (g フラグ) を付けた正規表現を指定する必要があります。

“`javascript
const text = “I have an apple, and you have an apple too.”;

// replaceに正規表現(gフラグ付き)を使用
const newTextReplaceAll = text.replace(/apple/g, “orange”);
console.log(newTextReplaceAll); // 出力: “I have an orange, and you have an orange too.”
“`

このように、replace メソッドで文字列中の全てを置換したい場合、検索対象が単純な文字列であっても、それを正規表現リテラル /apple/g のような形式で記述し、さらに g フラグを明示的に指定する必要がありました。これは、文字列をすべて置換したいという単純な要求に対して、正規表現という少し複雑な概念を持ち出す必要があり、直感的ではありませんでした。

また、replace に正規表現を指定する際、g フラグを付け忘れると、意図せず最初の一致しか置換されないというバグを生む可能性もありました。

replaceAll メソッドは、このような「すべてを置換したい」というニーズに特化して設計されました。 replaceAll は、第一引数に指定された検索対象(文字列または正規表現)と一致する部分を、文字列全体の中から漏れなくすべて見つけ出し、第二引数で指定された内容に置き換えます。

“`javascript
const text = “I have an apple, and you have an apple too.”;

// replaceAllを使用
const newTextReplaceAllModern = text.replaceAll(“apple”, “orange”);
console.log(newTextReplaceAllModern); // 出力: “I have an orange, and you have an orange too.”
“`

このように、replaceAll を使えば、検索対象が文字列の場合に、正規表現や g フラグといった知識なしに、直感的に「すべて置換」を実行できます。これが replaceAll が導入された最大の理由です。

ただし、replaceAll を正規表現で使う場合には重要な注意点があります。これについては後述します。

2. String.prototype.replaceAll() の基本

replaceAll() メソッドは、String オブジェクトのプロトタイプに追加されたメソッドです。つまり、すべての文字列インスタンスから直接呼び出すことができます。

構文:

javascript
string.replaceAll(searchValue, replaceValue);

  • string: メソッドを呼び出す元の文字列。
  • searchValue: 検索対象。置換したい部分を指定します。文字列 または 正規表現 (RegExp) オブジェクトを指定できます。
  • replaceValue: 置換後の内容。searchValue と一致した部分に代わって挿入される文字列、または置換後の文字列を返す関数 を指定できます。
  • 戻り値: すべての一致が置換された新しい文字列を返します。元の文字列は変更されません。

重要な点として、JavaScriptの文字列はイミュータブル(immutable)です。つまり、一度作成された文字列の内容は変更できません。replaceAll を含むすべての文字列メソッドは、元の文字列を変更するのではなく、常に新しい文字列を生成して返します。

3. searchValue に文字列を指定する使い方

searchValue に文字列を指定するのが、replaceAll の最も基本的で分かりやすい使い方です。指定した文字列と完全に一致する部分が、文字列全体の中からすべて見つけ出され、置換されます。大文字・小文字は区別されます(デフォルトの場合)。

例1:単純な文字列置換

“`javascript
const sentence = “Hello world, hello universe, hello everyone!”;
const replacedSentence = sentence.replaceAll(“hello”, “hi”);

console.log(sentence); // 出力: “Hello world, hello universe, hello everyone!” (元の文字列は変更されない)
console.log(replacedSentence); // 出力: “Hello world, hi universe, hi everyone!”
“`

この例では、小文字の “hello” がすべて “hi” に置換されました。最初の “Hello” (先頭が大文字) は一致しないため置換されません。

例2:複数の単語の置換

“`javascript
const list = “item1,item2,item3,item4”;
const commaSeparated = list.replaceAll(“,”, “;”);

console.log(commaSeparated); // 出力: “item1;item2;item3;item4”
“`

カンマ区切りのリストをセミコロン区切りに変換するなど、区切り文字の一括置換に便利です。

例3:特定の文字の削除

空文字列 ("") を replaceValue に指定することで、特定の部分文字列を削除できます。

“`javascript
const phoneNumber = “012-345-6789”;
const digitsOnly = phoneNumber.replaceAll(“-“, “”);

console.log(digitsOnly); // 出力: “0123456789”
“`

電話番号からハイフンを削除するなどの用途に使えます。

このように、searchValue に文字列を指定する方法は、直感的で分かりやすく、「この文字列を、この文字列に、ぜんぶ置き換えたい!」という場合に最適です。replace メソッドで文字列検索を行う場合は、最初のマッチしか置換されない(歴史的な理由による挙動)という混乱の元でしたが、replaceAll はこの点を明確に解決しています。

4. searchValue に正規表現を指定する使い方

replaceAllsearchValue には正規表現オブジェクト (RegExp) を指定することもできます。これにより、より複雑なパターンマッチングに基づいた置換が可能になります。

構文:

javascript
string.replaceAll(regex, replaceValue);

  • regex: 検索対象の正規表現オブジェクト。ただし、重要な制約があります(後述)。
  • replaceValue: 置換後の文字列または関数。

【最重要注意点】正規表現には必ず g (global) フラグが必要!

replaceAll メソッドで searchValue に正規表現を指定する場合、その正規表現オブジェクトには 必ずグローバル検索フラグ (g) が付いている必要があります

もし g フラグがない正規表現を replaceAllsearchValue に指定すると、TypeError が発生します。

“`javascript
const text = “abc abc abc”;

// gフラグなしの正規表現 – replaceAllではエラーになる!
try {
const result = text.replaceAll(/abc/, “xyz”);
console.log(result);
} catch (e) {
console.error(“エラーが発生しました:”, e.message); // 出力例: “エラーが発生しました: String.prototype.replaceAll requires a global RegExp”
}

// gフラグありの正規表現 – 正しく動作する
const result = text.replaceAll(/abc/g, “xyz”);
console.log(result); // 出力: “xyz xyz xyz”
“`

なぜ g フラグが必須なのか?

これは replaceAll の設計思想に基づいています。replaceAll はその名前の通り「すべて」を置換することを目的としています。正規表現において「すべて」を検索・マッチさせるためには、g フラグが必要です。

もし g フラグのない正規表現を許容すると、正規表現は最初に見つかった一致で検索を終了します(これが正規表現のデフォルトの動作です)。replaceAll がこの正規表現を受け取った場合、すべてを置換するという自身の目的と、正規表現が最初の一つしかマッチしないという挙動の間で矛盾が生じます。

JavaScriptの設計者は、このような曖昧さや潜在的なバグを防ぐため、replaceAll に正規表現を指定する場合は、検索対象が「すべて」であることを正規表現自身の g フラグで明示的に示すことを必須としました。これにより、開発者は「この正規表現を使って、グローバルにすべてを検索し、置換する」という意図をコード上で明確に表現できるようになります。

対照的に、replace メソッドは g フラグがない正規表現を受け取ると、最初のマッチだけを置換してエラーにはなりません。これは replace が元々、最初のマッチだけを置換するという用途も想定されていたためです。しかし、この挙動が「すべて置換したいのに、なぜか最初の一つしか変わらない」という混乱を招くこともありました。replaceAll はこの混乱を避けるためのより明確なAPIと言えます。

g フラグ以外の正規表現フラグ

replaceAll は、g フラグが必須であることに加えて、他の正規表現フラグ (i, m, s, u, y) とも組み合わせて使用できます。

  • i (ignoreCase): 大文字・小文字を区別せずにマッチングを行います。
  • m (multiline): ^$ が行頭・行末にマッチするようになります(デフォルトでは文字列全体の先頭・末尾)。
  • s (dotAll): ドット (.) が改行文字を含むすべての文字にマッチするようになります。
  • u (unicode): Unicode のコードポイントを正しく扱います(絵文字などを正確にマッチさせたい場合に必要)。
  • y (sticky): 前回のマッチの直後の位置からのみマッチを開始します(g フラグと併用されることが多いですが、replaceAll の場合はgが必須なので常に併用)。

例:i フラグを使用

“`javascript
const text = “Apple pie, apple juice, Big Apple.”;

// 大文字・小文字を区別せず ‘apple’ を検索し置換
const replacedText = text.replaceAll(/apple/gi, “orange”);

console.log(replacedText); // 出力: “Orange pie, orange juice, Big Orange.”
“`

この例では、i フラグによって “Apple” も “apple” と同じように扱われ、すべて “orange” に置換されています。

正規表現の特殊文字のエスケープ

searchValue に文字列を指定する場合、 "." のようにドット文字を指定すればそのままドット文字として扱われます。しかし、searchValue に正規表現を指定する場合、ドット (.) は「任意の一文字」という特別な意味を持ちます。他の多くの文字 (*, +, ?, ^, $, {, }, (, ), |, [, ], \) も正規表現では特殊な意味を持ちます。

もし、これらの特殊文字自体をリテラルとして検索したい場合は、バックスラッシュ (\) を使ってエスケープする必要があります。例えば、文字列中の . を検索したい場合は、正規表現では /\./g と記述します。

“`javascript
const version = “1.2.3”;

// 文字列の “.” を正規表現の任意の一文字として扱うと意図しない結果に
// const wrongResult = version.replaceAll(/./g, “-“); // エラーにならないが、すべての文字が置換される

// 文字列の “.” をリテラルとして検索するにはエスケープが必要
const correctResult = version.replaceAll(/./g, “-“);
console.log(correctResult); // 出力: “1-2-3”
“`

もし、ユーザー入力など、動的に生成される文字列を正規表現として検索したい場合は、その文字列に含まれる可能性のあるすべての正規表現特殊文字を適切にエスケープする処理が必要になります。JavaScriptの標準機能には便利なエスケープ関数はありませんが、手動で行うか、ライブラリを利用することができます。

5. replaceValue に文字列を指定する使い方(特殊パターン)

replaceAllreplaceValue に文字列を指定する場合、単に固定の文字列に置き換えるだけでなく、置換文字列内で特殊なパターンを使って、マッチした元の文字列や、正規表現を使った場合のキャプチャグループを参照することができます。これらのパターンは、$(ドル記号)で始まります。

これは String.prototype.replace() メソッドで使われる特殊パターンと全く同じものです。replaceAll も同じ挙動をします。

利用可能な特殊パターンは以下の通りです。

  • $$: 単一のドル記号 ($) を挿入します。
  • $&: マッチした部分文字列全体を挿入します。
  • $n または $nn: n番目またはnn番目のキャプチャグループ(丸括弧 () で囲まれた部分)にマッチした文字列を挿入します。nやnnは1から始まります。グループが存在しない場合は空文字列が挿入されます。
  • $<name>: 名前付きキャプチャグループ ((?<name>...)) にマッチした文字列を挿入します。グループが存在しない場合は空文字列が挿入されます。
  • $`: マッチした部分の直前にある文字列を挿入します。
  • $': マッチした部分の直後にある文字列を挿入します(アポストロフィ ' です)。

これらの特殊パターンを使うには、searchValue に正規表現(もちろん g フラグ付き)を指定する必要があります。文字列を searchValue にした場合、これらの特殊パターンは単なるリテラル文字列として扱われます。

例1:$& – マッチした部分全体を参照

“`javascript
const text = “The price is $100 and tax is $10.”;
// ドル記号の後ろに “USD” を追加したい
const newText = text.replaceAll(/\$(\d+)/g, “$& USD”);

console.log(newText); // 出力: “The price is $100 USD and tax is $10 USD.”
“`

ここでは、正規表現 /\$(\d+)/g$100$10 にマッチします。$& はそれぞれマッチした $100$10 を参照するため、置換文字列 "$^ USD" はそれぞれのマッチに対して $100 USD$10 USD に展開されます。

例2:$n – キャプチャグループを参照

“`javascript
const dateString = “2023-10-26”;
// YYYY-MM-DD 形式を MM/DD/YYYY 形式に変換したい
// キャプチャグループ: 1:年 (\d{4}), 2:月 (\d{2}), 3:日 (\d{2})
const newDateString = dateString.replaceAll(/(\d{4})-(\d{2})-(\d{2})/g, “$2/$3/$1”);

console.log(newDateString); // 出力: “10/26/2023”
“`

この例では、日付の各部分(年、月、日)をキャプチャグループとして取得し、$1, $2, $3 で参照して新しい形式の文字列を生成しています。

例3:$<name> – 名前付きキャプチャグループを参照

名前付きキャプチャグループは、番号ではなく名前で参照できるため、正規表現が複雑になった場合に可読性が向上します。

“`javascript
const dateString = “Year: 2023, Month: 10, Day: 26”;
// 日付の情報を抽出し、MM/DD/YYYY 形式に変換したい
const newDateString = dateString.replaceAll(
/Year: (?\d{4}), Month: (?\d{2}), Day: (?\d{2})/g,
“Converted Date: $/$/$
);

console.log(newDateString); // 出力: “Converted Date: 10/26/2023”
“`

正規表現中の (?<year>...) のように記述することで名前付きキャプチャグループを作成し、置換文字列では $<year> のように名前で参照します。

例4:`$' – 直前・直後の文字列を参照

これらのパターンは、マッチした部分のコンテキストを取得するのに使えます。

``javascript
const sentence = "Before match, MATCH, After match.";
// "MATCH" を見つけて、その前後をログ出力したい(ただし置換自体は別の文字列に)
const newSentence = sentence.replaceAll(/MATCH/g, (match, offset, originalString) => {
// 関数置換の方が前後文字列の取得には便利ですが、ここでは$パターン文字列置換の例として$
, $’の説明をします。
// 実際のコードでは、$, $' を置換文字列に含めると、その位置の前後文字列が挿入されます。
// 例: “>>>$<<< MATCH >>>$'<<<"
// これはあまり一般的な使い方ではないかもしれません。ログ出力などには関数置換が向いています。
// ここでは、$
と $’ がどのような文字列に展開されるかを示すために、単純な置換例を挙げます。

// replaceValueを文字列として指定する場合の例
// "マッチしました。直前: [$`] 直後: [$']"
return "FOUND"; // 単純な置換文字列にする

});
console.log(newSentence); // 出力: “Before match, FOUND, After match.”

// $と $' が文字列に展開される様子を確認するための例(このパターンでの置換は少ないかも)
const testString = "abc-def-ghi";
const resultWithContext = testString.replaceAll(/-def-/g, "[$
:$’]”);
console.log(resultWithContext); // 出力: “abc[abc:ghi]ghi”
// ここでは、-def- にマッチした際の $`` は "abc" に、$' は “ghi” に展開されています。
“`

`$' パターンは、正規表現のマッチ位置の前後にある文字列全体を参照します。これは、特定のパターンを見つけた際に、そのコンテキスト情報を含んだ置換文字列を生成したい場合に理論上使えますが、実際には関数置換 (replaceValue に関数を指定する方法) の方が、引数としてオフセットや元の文字列全体を受け取れるため、より柔軟にこれらの情報を利用できます。

例5:$$ – ドル記号そのものを挿入

置換文字列内で $1$& といった特殊パターンではなく、単にリテラルのドル記号 $ を挿入したい場合は、$$ と記述します。

“`javascript
const amount = “Price: 100”;
// 数値の前にドル記号を付けたい
const newAmount = amount.replaceAll(/(\d+)/g, “$$$1”); // $$ で $ を、$1 でキャプチャした数値を挿入

console.log(newAmount); // 出力: “Price: $100”
“`

正規表現 (\d+) が数値 100 にマッチします。$$$1 は、$$$ に、$1 がキャプチャした数値 100 に展開され、結果として $100 という文字列が生成されます。

これらの特殊パターンを使いこなすと、正規表現を使った置換が非常に強力になります。ただし、これらのパターンは replaceValue文字列として指定された場合のみ有効であり、後述する関数が指定された場合は、関数が受け取る引数としてマッチ情報などが渡されるため、このようなパターンは使用しません。

6. replaceValue に関数を指定する使い方

replaceAllreplaceValue には、文字列の代わりに関数を指定することもできます。この関数は、searchValue に指定されたパターン(文字列または正規表現)が文字列中で見つかるすべての一致に対して、それぞれ一度ずつ呼び出されます。

関数の戻り値が、そのマッチした部分の代わりに挿入される置換文字列となります。

関数の引数:

replaceValue に指定する関数は、マッチが見つかるたびに呼び出され、通常、以下の引数を受け取ります。

javascript
function replacer(match, p1, p2, ..., offset, originalString, namedCaptures) {
// 置換後の文字列を返す
}

引数の意味は以下の通りです。

  1. match: searchValue とマッチした部分文字列全体。($& パターンに相当)
  2. p1, p2, ...: searchValue に正規表現を指定した場合の、n番目、n+1番目… のキャプチャグループにマッチした部分文字列。キャプチャグループの数だけ引数が並びます。キャプチャグループがない場合、これらの引数は存在しません。
  3. offset: マッチが見つかった位置(元の文字列のインデックス)。($ パターンに相当)
  4. originalString: 元の文字列全体。($' パターンもこの文字列から計算できます)
  5. namedCaptures: searchValue に名前付きキャプチャグループを含む正規表現を指定した場合、その結果を含むオブジェクト。($<name> パターンに相当) この引数は、名前付きキャプチャグループが1つ以上存在する場合にのみ提供されます。

関数置換の利点:

  • 動的な置換: マッチした内容やその位置、周囲の文字列など、様々な情報に基づいて、置換する文字列を動的に生成できます。
  • 複雑なロジック: 単純な文字列変換だけでなく、条件分岐を行ったり、外部のデータを利用したりといった複雑な置換処理を実行できます。

これは String.prototype.replace() で関数を指定する場合と全く同じ挙動です。replaceAll の場合は、この関数がすべての一致に対して確実に呼び出される点が異なります(replaceg フラグがない正規表現や文字列検索の場合は最初のマッチでしか関数が呼ばれません)。

例1:マッチした文字列を大文字に変換

``javascript
const text = "hello world";
// 'o' を見つけるたびに関数を呼び出し、その戻り値 ('O') に置換
const newText = text.replaceAll("o", (match) => {
console.log(
マッチしました: ${match}`); // ‘o’ が見つかるたびにログ出力
return match.toUpperCase();
});

console.log(newText); // 出力: “hellO wOrld”
“`

この例では、searchValue に文字列を指定しています。この場合でも、replaceAll は文字列全体をスキャンし、一致 ("o") を見つけるたびに関数を呼び出します。関数は引数 match として "o" を受け取り、その大文字版 "O" を返しています。

例2:正規表現とキャプチャグループを使った動的な置換

``javascript
const items = "Item A (Price: 100), Item B (Price: 250), Item C (Price: 50)";
// 各アイテムの価格を2倍にしたい
const updatedItems = items.replaceAll(/\(Price: (\d+)\)/g, (match, price, offset, originalString) => {
console.log(
マッチ: ${match}, 価格: ${price}, 位置: ${offset});
const newPrice = parseInt(price, 10) * 2;
return
(Price: ${newPrice})`;
});

console.log(updatedItems); // 出力: “Item A (Price: 200), Item B (Price: 500), Item C (Price: 100)”
“`

この例では、正規表現 /\(Price: (\d+)\)/g を使用しています。
g フラグが付いているため、すべての一致 ((Price: 100), (Price: 250), (Price: 50)) に対して関数が呼び出されます。
– 関数は以下の引数を受け取ります:
match: マッチ全体 (例: "(Price: 100)")
price: 最初のキャプチャグループ (\d+) にマッチした部分 (例: "100")
offset: マッチの開始位置 (例: 10)
originalString: 元の文字列全体
関数内で、取得した価格 (price) を整数に変換して2倍し、新しい価格を含む文字列を生成して返しています。

例3:名前付きキャプチャグループと関数

``javascript
const logEntry = "user: alice, id: 123; user: bob, id: 456;";
// 各ユーザーIDにプレフィックスを付けたい
const updatedLog = logEntry.replaceAll(
/user: (?<username>\w+), id: (?<userId>\d+);/g,
(match, username, userId, offset, originalString, namedCaptures) => {
console.log(
マッチ: ${match});
console.log("名前付きキャプチャ:", namedCaptures); // { username: "alice", userId: "123" } など
const newId =
ID-${namedCaptures.userId};
return
user: ${namedCaptures.username}, id: ${newId};`;
}
);

console.log(updatedLog);
// 出力: “user: alice, id: ID-123; user: bob, id: ID-456;”
“`

名前付きキャプチャグループ (?<username>\w+)(?<userId>\d+) を使用しています。関数は最後の引数 namedCaptures として { username: "...", userId: "..." } のようなオブジェクトを受け取ります。これにより、名前を使ってキャプチャグループの値にアクセスできるため、コードの可読性が向上します。

例4:オフセット引数の利用

``javascript
const quote = "The quick brown fox jumps over the lazy fox.";
// 2回目以降の "fox" のみを置換したい(オフセットを利用)
const newQuote = quote.replaceAll(/fox/g, (match, offset, originalString) => {
console.log(
‘fox’ found at offset: ${offset}`);
// 最初の ‘fox’ は offset が 16
// 2番目の ‘fox’ は offset が 40
if (offset > 20) { // 例えば、特定のインデックス以降のみ置換
return “dog”;
}
return match; // 置換しない場合は元のマッチを返す
});

console.log(newQuote); // 出力: “The quick brown fox jumps over the lazy dog.”
“`

offset 引数を使って、マッチした位置に基づいて置換するかどうかを条件分岐させています。この例では、最初の fox (オフセット16) はそのまま残し、2番目の fox (オフセット40) のみを置換しています。

このように、replaceValue に関数を指定することで、非常に柔軟で複雑な置換ロジックを実現できます。これは replaceAll の最も強力な使い方の1つと言えます。

7. replaceAll() の注意点まとめ

これまでに解説した内容を含め、replaceAll() を使う上で特に注意すべき点をまとめます。

  1. 正規表現に g フラグが必須: これは最も重要な注意点です。searchValue に正規表現を指定する場合、必ず /.../g または new RegExp(..., 'g') のように g フラグを含める必要があります。含めないと TypeError がスローされます。
  2. 互換性: replaceAll() は比較的新しい機能であり、ES2021 (ECMAScript 2021) で標準化されました。 Internet Explorer などの古いブラウザではサポートされていません。 Node.js では v15.0.0 以降でサポートされています。
    • ターゲットとする環境が古い場合、replaceAll の代わりに string.replace(/.../g, ...) を使用するか、Polyfill (後述) を利用する必要があります。
  3. 元の文字列は変更されない (イミュータブル): replaceAll() は常に新しい文字列を生成して返します。元の文字列は全く変更されません。置換結果を利用するには、戻り値を新しい変数に代入する必要があります。
  4. replace との違いの再確認:
    • 文字列検索: str.replace("a", "b") は最初の “a” のみ置換。str.replaceAll("a", "b") はすべての “a” を置換。
    • 正規表現検索 (g フラグなし): str.replace(/a/, "b") は最初の “a” のみ置換し、エラーにならない。str.replaceAll(/a/, "b")TypeError になる。
    • 正規表現検索 (g フラグあり): str.replace(/a/g, "b") はすべての “a” を置換し、str.replaceAll(/a/g, "b") もすべての “a” を置換する。この場合、結果は同じになりますが、replaceAll の方が「すべて置換する」という意図がより明確です。
  5. nullundefined に対する呼び出し: replaceAll はプリミティブの String オブジェクトのメソッドです。nullundefined は文字列ではないため、これらの値に対して直接 replaceAll を呼び出すと TypeError になります。これは replace や他の文字列メソッドと同様です。
    javascript
    let myVar = null;
    // myVar.replaceAll("a", "b"); // TypeError

    安全に呼び出すには、対象が文字列であることを確認するか、文字列に変換してから呼び出す必要があります (String(myVar).replaceAll(...))。ただし、多くの場合、このような操作が必要な設計は避けるべきです。
  6. パフォーマンス: 非常に長い文字列に対して複雑な正規表現で replaceAll を実行する場合、パフォーマンスが問題になる可能性があります。ほとんどの一般的なユースケースでは心配ありませんが、大規模なテキスト処理を行う場合は、処理時間やメモリ使用量を考慮する必要があるかもしれません。ただし、多くの場合、適切に書かれた正規表現を使った replaceAll は、自前でループを回して置換処理を実装するよりも効率的です。

8. replaceAll の代替手段(古い環境など)

replaceAll が利用できない環境(特に古いブラウザ)で「すべて置換」を実現する必要がある場合、いくつかの代替手段があります。

  1. String.prototype.replace()g フラグ付き正規表現:
    これが最も一般的で推奨される代替手段です。文字列検索の場合も、対象の文字列を正規表現に変換し、g フラグを付けます。
    “`javascript
    const text = “apple apple apple”;
    // 文字列 “apple” を検索する場合、正規表現に変換してgフラグを付ける
    const escapedSearch = “apple”.replace(/[.*+?^${}()|[]\]/g, ‘\$&’); // 特殊文字をエスケープ
    const regex = new RegExp(escapedSearch, ‘g’);
    const newText = text.replace(regex, “orange”);
    console.log(newText); // 出力: “orange orange orange”

    // 正規表現を検索する場合
    const anotherText = “ver1.2.3 ver4.5.6”;
    const newAnotherText = anotherText.replace(/ver(\d+.\d+.\d+)/g, “version $1”);
    console.log(newAnotherText); // 出力: “version 1.2.3 version 4.5.6”
    “`
    この方法では、検索文字列に正規表現の特殊文字が含まれる場合に適切にエスケープする必要があるという手間が発生します。

  2. split()join() の組み合わせ (文字列検索の場合):
    これは検索対象が文字列の場合のみ有効な方法です。元の文字列を検索文字列で分割し、その配列を置換文字列で結合し直します。
    javascript
    const text = "apple,banana,apple,grape";
    const newText = text.split("apple").join("orange");
    console.log(newText); // 出力: "orange,banana,orange,grape"

    この方法はシンプルで分かりやすいですが、正規表現を使った複雑なパターン検索や、置換文字列に関数や $1 のような特殊パターンを使いたい場合には利用できません。また、検索対象が空文字列 ("") の場合は意図しない結果になる可能性が高いです。

  3. Polyfill の利用:
    多くのモダンなJavaScript機能と同様に、replaceAll にも Polyfill が存在します。Polyfill は、古い環境で新しい機能と同等の機能を提供するコードです。Polyfill をプロジェクトに含めることで、古い環境でも replaceAll メソッドをそのまま使用できるようになります。
    例えば、core-js のようなライブラリは、多くの新しいJavaScript機能の Polyfill を提供しています。
    “`javascript
    // core-js の replaceAll Polyfill をインポート (例: モジュールバンドラーを使用している場合)
    // import ‘core-js/actual/string/replace-all’;

    // またはスクリプトタグで読み込む

    const text = “apple apple”;
    // 古い環境でも replaceAll が使えるようになる
    const newText = text.replaceAll(“apple”, “orange”);
    console.log(newText);
    ``
    Babelのようなトランスパイラを使用する場合、適切な設定 (
    @babel/preset-envuseBuiltIns`) を行えば、ターゲット環境に合わせて必要な Polyfill が自動的に追加されます。

ターゲットとする環境やプロジェクトの複雑さに応じて、これらの代替手段から最適なものを選ぶことができます。モダンな環境での開発であれば、特別な理由がない限り replaceAll をそのまま使うのが最も推奨されます。

9. 高度な使用例

replaceAll は様々なテキスト処理に応用できます。いくつかの高度な使用例を見てみましょう。

例1:簡単なテンプレート置換

特定のプレースホルダーを値に置き換える簡単なテンプレートエンジンとして利用できます。

“`javascript
const template = “Hello, {{name}}! Your age is {{age}}.”;
const data = { name: “Alice”, age: 30 };

let result = template;
// オブジェクトのキーをループして置換
for (const key in data) {
// 正規表現を動的に生成(プレースホルダーを検索)
// RegExp コンストラクタを使う場合は、特殊文字をエスケープする必要がある
const escapedKey = key.replace(/[.*+?^${}()|[]\]/g, ‘\$&’);
const placeholderRegex = new RegExp(\\{\\{${escapedKey}\\}\\}, ‘g’);

result = result.replaceAll(placeholderRegex, data[key]);

}

console.log(result); // 出力: “Hello, Alice! Your age is 30.”
“`

この例では、{{key}} 形式のプレースホルダーを対応する data オブジェクトの値に置き換えています。正規表現を使って {{}} を正確にマッチさせています。正規表現の動的生成には RegExp コンストラクタを使用し、キーに含まれる可能性のある特殊文字をエスケープしています。

例2:HTMLタグのサニタイズ(基本的なエスケープ)

ユーザー入力などを表示する際に、HTMLタグとして解釈される可能性のある文字(<, >, &, ", ')をエスケープして、クロスサイトスクリプティング (XSS) 攻撃を防ぐ基本的な対策として利用できます。

“`javascript
function escapeHTML(text) {
if (typeof text !== ‘string’) {
return ”;
}
let escaped = text.replaceAll(‘&’, ‘&’); // & を先にエスケープ
escaped = escaped.replaceAll(‘<‘, ‘<‘);
escaped = escaped.replaceAll(‘>’, ‘>’);
escaped = escaped.replaceAll(‘”‘, ‘"’);
escaped = escaped.replaceAll(“‘”, ‘'’); // または ' ですが、互換性のため数値参照もよく使われる
return escaped;
}

const userInput = ‘ & “quoted” \’text\”;
const safeOutput = escapeHTML(userInput);

console.log(safeOutput);
// 出力: “<script>alert("XSS!")</script> & "quoted" 'text'”
“`

この例では、複数の replaceAll 呼び出しをチェーンして、各特殊文字を対応するHTMLエンティティに置き換えています。& を最初にエスケープすることが重要です。例えば、&amp;< のエスケープである &lt; の後にエスケープしようとすると、意図せず &amp;lt; のようになってしまいます。

これはあくまで基本的なエスケープであり、より堅牢なサニタイズやHTMLパースには専用のライブラリを使用することを推奨します。

例3:特定の文字範囲の削除

正規表現を使って、特定の文字コード範囲にある文字を一括削除することも可能です。例えば、制御文字や非表示文字などを削除したい場合などです。

“`javascript
// ASCIIの制御文字 (0x00 – 0x1F) と一部のその他の非表示文字などを削除
const textWithControlChars = “Hello\u0000World\u001fThis is a test.\u200b”;
const cleanedText = textWithControlChars.replaceAll(/[\u0000-\u001f\u200b]/g, “”);

console.log(cleanedText); // 出力: “HelloWorldThis is a test.”
“`

正規表現の文字クラス [] とユニコードエスケープ \uXXXX を利用して、削除したい文字の範囲を指定しています。g フラグで全てを置換しています。

これらの例は、replaceAll と正規表現や関数置換を組み合わせることで、文字列に対して柔軟かつ強力な操作が可能になることを示しています。

10. まとめ

String.prototype.replaceAll() メソッドは、文字列中のすべての一致する部分を指定した内容に置き換えるための、シンプルで意図が明確な新しいAPIです。

  • searchValue文字列を指定すると、その文字列と一致する部分がすべて置換されます。これは replace との最も明確な違いの一つであり、単純な文字列置換のニーズに直感的に応えます。
  • searchValue正規表現を指定する場合は、必ず g (global) フラグを含める必要があります。これは replaceAll の設計思想であり、含めないと TypeError が発生します。
  • replaceValue には文字列または関数を指定できます。
    • 文字列を指定する場合、$$, $&, $1, $<name>, `, $' といった特殊パターンを使って、マッチした情報に基づいて動的に置換文字列を生成できます(正規表現検索時のみ有効)。
    • 関数を指定する場合、マッチした部分やキャプチャグループ、位置などの情報を受け取り、複雑なロジックに基づいて置換文字列を動的に生成できます。この関数は、すべての一致に対して呼び出されます。
  • replaceAll は常に新しい文字列を返します。元の文字列は変更されません。
  • 古い環境ではサポートされていないため、必要に応じて replace (w/ g flag) や Polyfill を使用する必要があります。

replaceAll は、特に「文字列中の何かを、見つかったところ全部、置き換えたい」という場合に、replace を使うよりもコードの意図を明確に表現できます。モダンなJavaScript開発においては、この目的で replace(/.../g, ...) を使う代わりに replaceAll を使うことが推奨されます。

付録:replacereplaceAll の比較表

特徴 String.prototype.replace() String.prototype.replaceAll()
目的 最初の一致を置換、または g フラグ付きで全て置換 すべての一致を確実に置換
searchValue が文字列 最初の一致のみ置換 すべての一致を置換
searchValue が正規表現 ( g フラグなし) 最初の一致のみ置換、エラーにならない TypeError がスローされる (g フラグ必須)
searchValue が正規表現 ( g フラグあり) すべての一致を置換 すべての一致を置換 (推奨される書き方)
replaceValue が文字列 $, $&, $n などの特殊パターンが利用可能 $, $&, $n などの特殊パターンが利用可能
replaceValue が関数 マッチが見つかるたびに関数が呼ばれる
(gなし正規表現/文字列検索では最初の1回のみ)
マッチが見つかるたびに関数が呼ばれる
(すべての一致に対して)
互換性 広くサポートされている 比較的新しい (ES2021)。古い環境ではPolyfillが必要。
コードの意図の明確さ 「すべて置換」にはgフラグが必要で少し分かりにくい 「すべて置換」に特化しており意図が明確

この比較表からもわかるように、replaceAll は「すべて置換」という特定のユースケースにおいて、replace よりも直感的で安全な選択肢を提供します。

これで、JavaScriptの replaceAll メソッドについて、基本的な使い方から応用、そして重要な注意点まで、完全に理解できたことでしょう。今後の開発で積極的に活用してみてください。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール