String replaceAll / matchAllLoading saved progress…

String replaceAll / matchAll

replaceAll and matchAll are the "operate on every match" string methods: replaceAll returns a new string with every occurrence of a pattern swapped out, and matchAll returns an iterator over every match a global regex finds. Their single-match cousins, replace and match, stop after the first hit unless you remember the g flag — the footgun these two exist to remove. Build both without calling the real String.prototype.replaceAll or String.prototype.matchAll; regexp.exec and String.prototype.indexOf are the primitives you orchestrate.

Package the two as a single object, stringReplaceMatchAll = { replaceAll, matchAll }.

Signature

replaceAll(str, pattern, replacement) // → a new string, every occurrence replaced
matchAll(str, regexp)                 // → an iterator of match objects
  • pattern is a literal string or a global RegExp; a non-global RegExp throws a TypeError.
  • replacement is a string (supporting $&, $1..$9, and $$) or a function called once per match.
  • regexp must be global; each yielded match has [0] (full match), [1..n] (capture groups), .index, and .input.

Examples

const { replaceAll, matchAll } = stringReplaceMatchAll;

replaceAll('a.b.a', 'a', 'X');    // 'X.b.X'   every literal 'a'
replaceAll('a.b.c', '.', '-');    // 'a-b-c'   a string pattern is literal, not "any char"
replaceAll('a1b2c3', /\d/g, '#'); // 'a#b#c#'  a global regex
replaceAll('x1', /x/, '#');       // throws TypeError — the regex is not global
[...matchAll('a1b2', /([a-z])(\d)/g)];
// two matches:
//   match[0] === 'a1', match[1] === 'a', match[2] === '1', match.index === 0
//   match[0] === 'b2', match[1] === 'b', match[2] === '2', match.index === 2

[...matchAll('abc', /\d/g)]; // []  — no matches, an empty sequence

Notes

  • Every occurrence — both methods sweep the whole string; they never stop at the first hit.
  • String patterns are literalreplaceAll('a.b', '.', '-') replaces the real dot only; a regex pattern is matched by its rules and must carry the g flag.
  • Non-global regex throws — a RegExp without g is a TypeError in both methods, exactly as the real ones behave.
  • Function replacementreplacement may be a function called per match with (match, ...groups, offset, string); its return value is inserted.
  • matchAll returns an iterator — spread it or for..of it; each match carries [0], its capture groups, .index, and .input.
  • Do not use the built-ins — no real replaceAll / matchAll; regexp.exec and indexOf are the primitives.

FAQ

Why does replaceAll throw on a non-global regex?
A regex without the g flag matches only once, so replaceAll would silently replace a single occurrence instead of all of them. Rather than hide that footgun, replaceAll throws a TypeError and forces you to add the g flag.
What is the zero-length match infinite loop in matchAll?
A pattern like /a*/g can match the empty string at a position without consuming a character, so the regex lastIndex never advances and exec returns the same empty match forever. The fix is to manually bump lastIndex by one whenever a match is empty.
How is a string pattern different from a regex pattern in replaceAll?
A string pattern is matched literally, so replaceAll('a.b', '.', '-') only replaces the real dot. A regex pattern is matched by its rules and must carry the g flag, so /./g would replace every character instead.
Loading editor…