Skip to content
JavaScript js regex 4 min read

Regex Methods in Practice

A regular expression is only useful once you run it against a string, and JavaScript spreads that work across two objects: RegExp (with test and exec) and String (with match, matchAll, replace, replaceAll, and split). Each method answers a different question — does it match?, what matched?, replace what matched, or break the string apart. Picking the right one keeps your code clear and avoids subtle bugs with global flags and stateful lastIndex. This page walks through each method with practical validation, extraction, and formatting examples.

Testing for a match: test and exec

RegExp.prototype.test returns a plain boolean and is the fastest way to validate input. Use it whenever you only care whether a pattern is present.

const isHexColor = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;

console.log(isHexColor.test("#1a2b3c")); // true
console.log(isHexColor.test("blue"));    // false

Output:

true
false

RegExp.prototype.exec returns a match array (or null), including captured groups. With the global (g) flag it advances lastIndex on each call, letting you loop through every match — but that statefulness is also a classic gotcha.

const re = /(\w+)@(\w+\.\w+)/g;
const text = "Reach [email protected] or [email protected]";
let m;

while ((m = re.exec(text)) !== null) {
  console.log(`full=${m[0]} user=${m[1]} host=${m[2]}`);
}

Output:

[email protected] user=a host=x.io
[email protected] user=b host=y.dev

Reusing a global regex with test/exec mutates lastIndex. If you call test twice on the same literal in a loop, results alternate. For one-off checks, drop the g flag or reset re.lastIndex = 0.

Extracting matches: match and matchAll

String.prototype.match behaves differently depending on the flag. Without g it returns a single rich match array with capture groups (like exec). With g it returns a flat array of all full matches and discards the groups.

const date = "2026-06-01";
const parts = date.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(parts[1], parts[2], parts[3]); // year month day

const tags = "<p><span><a>".match(/<(\w+)>/g);
console.log(tags); // groups lost, only full matches

Output:

2026 06 01
[ '<p>', '<span>', '<a>' ]

When you need every match and its groups, reach for String.prototype.matchAll, which returns an iterator of full match arrays. Pair it with named groups for readable extraction.

const log = "GET /home 200, POST /api 201";
const re = /(?<method>\w+) (?<path>\/\w+) (?<status>\d+)/g;

for (const { groups } of log.matchAll(re)) {
  console.log(`${groups.method} ${groups.path} -> ${groups.status}`);
}

Output:

GET /home -> 200
POST /api -> 201

matchAll requires the g flag and throws a TypeError without it. Unlike exec, it does not depend on lastIndex, so it is safe to use repeatedly.

MethodNeeds g?ReturnsKeeps groups?
testoptionalbooleanno
execoptionalarray or nullyes
matchoptionalarray or nullonly without g
matchAllrequirediterator of arraysyes

Replacing with capture refs and functions

String.prototype.replace accepts capture references in the replacement string: $1, $2 for numbered groups, $<name> for named groups, and $& for the whole match. Add the g flag (or use replaceAll) to replace every occurrence.

const us = "06/01/2026";
const iso = us.replace(/(\d{2})\/(\d{2})\/(\d{4})/, "$3-$1-$2");
console.log(iso); // reformatted to ISO

Output:

2026-06-01

For logic that a static string cannot express, pass a replacer function. It receives the full match, then each captured group, the offset, and the original string. Return the replacement.

const price = "Total: 1499 and 250 cents";

const formatted = price.replace(/\d+/g, (match) =>
  Number(match).toLocaleString("en-US")
);
console.log(formatted);

// Named groups arrive as the last argument
const masked = "card 4111222233334444".replace(
  /(?<keep>\d{4})\d+(?<last>\d{4})/,
  (_full, ...args) => {
    const { keep, last } = args.at(-1);
    return `${keep} **** **** ${last}`;
  }
);
console.log(masked);

Output:

Total: 1,499 and 250 cents
card 4111 **** **** 4444

Use String.prototype.replaceAll when your separator is a plain string, or when you want to guard against forgetting the g flag — replaceAll with a non-global regex throws, surfacing the mistake immediately.

Splitting strings

String.prototype.split accepts a regex separator, which is far more flexible than a fixed string. A common need is splitting on runs of whitespace or multiple delimiters at once.

const csv = "a, b ,c ,  d";
console.log(csv.split(/\s*,\s*/)); // trim around commas

console.log("one1two2three".split(/\d/)); // split on any digit

Output:

[ 'a', 'b', 'c', 'd' ]
[ 'one', 'two', 'three' ]

If the separator regex contains a capturing group, the captured text is included in the result array — handy for keeping delimiters.

console.log("3+4-5".split(/([+\-])/));

Output:

[ '3', '+', '4', '-', '5' ]

Best Practices

  • Use test for boolean validation and exec/matchAll when you need the captured data — don’t overfetch.
  • Prefer matchAll over a while loop with exec for global matching; it sidesteps lastIndex bugs and reads cleaner.
  • Never share a global (g) regex across calls to test/exec without resetting lastIndex, or define it inside the function scope.
  • Reach for named capture groups ((?<name>…)) and $<name> refs to keep replacements self-documenting.
  • Use a replacer function whenever the replacement depends on the matched content (formatting, casing, lookups).
  • Choose replaceAll for literal-string replacement; it fails fast if you accidentally pass a non-global regex.
  • Compile regex literals once (module scope) rather than rebuilding them inside hot loops.
Last updated June 1, 2026
Was this helpful?