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/execmutateslastIndex. If you calltesttwice on the same literal in a loop, results alternate. For one-off checks, drop thegflag or resetre.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
matchAllrequires thegflag and throws aTypeErrorwithout it. Unlikeexec, it does not depend onlastIndex, so it is safe to use repeatedly.
| Method | Needs g? | Returns | Keeps groups? |
|---|---|---|---|
test | optional | boolean | no |
exec | optional | array or null | yes |
match | optional | array or null | only without g |
matchAll | required | iterator of arrays | yes |
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
testfor boolean validation andexec/matchAllwhen you need the captured data — don’t overfetch. - Prefer
matchAllover awhileloop withexecfor global matching; it sidestepslastIndexbugs and reads cleaner. - Never share a global (
g) regex across calls totest/execwithout resettinglastIndex, 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
replaceAllfor 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.