JavaScriptRegex Methods (match, replace, exec)

Regex Methods (match, replace, exec)

Patterns are only half of regex. The other half is the small set of methods you call to actually do something with them — on the String prototype and on RegExp itself. This page walks through each one with a focus on which to reach for in which situation.

String.prototype.match

Without the g flag, match returns the first match with full details. With g, it returns an array of the matched strings only — no groups, no index.

JS
const text = "Order #42 shipped on 2026-05-13";

// No g — rich result for the first match
const m = text.match(/#(\d+)/);
console.log(m[0]);       // "#42"   — the whole match
console.log(m[1]);       // "42"    — first capture group
console.log(m.index);    // 6       — start position
console.log(m.input);    // the original string

// With g — only the matched strings, no groups
const all = text.match(/\d+/g);
console.log(all);        // ["42", "2026", "05", "13"]
No matches?
`match` returns `null` (not an empty array) when nothing matched. Always check before destructuring: `const m = s.match(re); if (!m) return;`
String.prototype.matchAll

When you want all matches with their groups, use matchAll. It returns an iterator of full match objects — the best of both worlds.

JS
const text = "Order #42 and #137 today";

for (const m of text.matchAll(/#(\d+)/g)) {
  console.log("match:", m[0], "id:", m[1], "at index", m.index);
}
match: #42 id: 42 at index 6
match: #137 id: 137 at index 14
The g flag is required
Calling `text.matchAll(/#(\d+)/)` without the `g` flag throws a `TypeError`. It is a deliberate "all" method.
String.prototype.replace and replaceAll

replace swaps the first match (or all matches with the g flag). replaceAll is the same as replace with a global regex — but it works for plain strings too, and requires the g flag when given a regex.

Basic replacement

JS
"banana".replace("a", "_");        // "b_nana"     — first only
"banana".replace(/a/g, "_");       // "b_n_n_"     — all
"banana".replaceAll("a", "_");     // "b_n_n_"     — same
// "banana".replaceAll(/a/, "_");  // TypeError    — needs g

The replacement string supports back-references to captured groups:

Capturing groups in replace

JS
// Swap "first last" -> "last, first"
"Ada Lovelace".replace(/^(\w+)\s+(\w+)$/, "$2, $1");
// "Lovelace, Ada"

// Named groups
"2026-05-13".replace(
  /(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/,
  "$<d>/$<m>/$<y>"
);
// "13/05/2026"

If you pass a function as the replacement, it is called for every match and its return value is used. The arguments are: full match, then each captured group, then the index, the input, and (if present) a groups object.

Function replacement

JS
const text = "prices: 12, 7, 100";

const doubled = text.replace(/\d+/g, (m) => Number(m) * 2);
console.log(doubled);   // "prices: 24, 14, 200"

const titled = "hello world".replace(
  /\b(\w)(\w*)/g,
  (_, first, rest) => first.toUpperCase() + rest
);
console.log(titled);    // "Hello World"
String.prototype.split

split cuts a string into pieces wherever the pattern matches. Passing a regex is more flexible than a literal string — you can split on any whitespace, on commas with optional spaces, on multiple delimiters at once.

JS
"a,b , c,  d".split(/\s*,\s*/);
// ["a", "b", "c", "d"]

"one two   three".split(/\s+/);
// ["one", "two", "three"]

// Capturing groups keep the delimiter in the output
"1+2-3".split(/([+\-])/);
// ["1", "+", "2", "-", "3"]
String.prototype.search

search returns the index of the first match, or -1 if there is none. It is the regex equivalent of indexOf.

JS
"prices: 12, 7, 100".search(/\d+/);   // 8
"no digits here".search(/\d+/);       // -1
RegExp.prototype.exec and lastIndex

exec is the oldest way to iterate matches. With a global (or sticky) regex, the engine remembers where it left off in regex.lastIndex and resumes from there on the next call.

JS
const re = /(\d+)/g;
const text = "a 12, b 7, c 100";

let m;
while ((m = re.exec(text)) !== null) {
  console.log(m[0], "at", m.index, "next from", re.lastIndex);
}
12 at 2 next from 4
7 at 8 next from 9
100 at 13 next from 16
Sharing a global regex
Because `lastIndex` is mutable state on the regex object, sharing a global regex across calls can produce surprising results. Either create a fresh regex each time, or use `String.matchAll` which gives you a clean iterator.
RegExp.prototype.test

test returns a boolean. Reach for it whenever you just want to validate.

JS
const isPositiveInt = /^[1-9]\d*$/;

isPositiveInt.test("42");    // true
isPositiveInt.test("0");     // false
isPositiveInt.test("-3");    // false
isPositiveInt.test("3.14");  // false
Which method should I use?
  • Just need yes/no? regex.test(str).

  • Need details of one match? str.match(regex) (without g).

  • Need every match, with groups? str.matchAll(regex) (g required).

  • Need to transform text? str.replace(regex, ...) — pass a function for anything non-trivial.

  • Need to split on a pattern? str.split(regex).

  • Building a tokenizer that resumes match-by-match? regex.exec with the g or y flag.

Beware of recreating regex literals in hot loops
A regex literal is parsed once when the program loads, so `/\d+/g.test(...)` is fine. But `new RegExp(userInput, "g")` inside a hot loop rebuilds the pattern every call. Cache it outside the loop and reuse.