Regexes are used through a handful of methods, some on the regex and some on strings. This lesson is the complete set, including the two most powerful: matchAll and replace with a function.
| Method | Returns | Use for |
|---|---|---|
| re.test(str) | true / false | Validation |
| re.exec(str) | one detailed match or null | Step-by-step matching |
| str.search(re) | position or -1 | Where is it? |
| str.match(re) | array or null | First match + groups, or all matches with g |
| str.matchAll(re) | iterator of matches | Every match + groups |
| str.replace(re, x) | new string | Swap text |
| str.replaceAll(re, x) | new string | Same, needs g |
| str.split(re) | array | Break text apart |
const s = "x=1, y=22";
s.match(/(\w)=(\d+)/); // ["x=1", "x", "1"] first match WITH groups
s.match(/(\w)=(\d+)/g); // ["x=1", "y=22"] all matches, NO groups
s.match(/z/); // null
match returns null, not an empty array, when nothing matches. s.match(/z/g).length throws a TypeError. Guard it: (s.match(/z/g) ?? []).length.
const prices = "Pens $2.50, Paper $4.25, Tape $1.99";
const re = /(?<item>\w+) \$(?<price>\d+\.\d{2})/g;
const items = [...prices.matchAll(re)].map(m => ({
item: m.groups.item,
price: Number(m.groups.price)
}));
console.log(items);
matchAll requires the g flag and throws a TypeError without it.
Both give you every match with its groups, but they work very differently. (W3Schools' reference mixes them up: exec() does not return an iterator.)
| exec() loop | matchAll() | |
|---|---|---|
| Returns | One match array per call, or null | An iterator of all match arrays |
| Needs g | Yes, or a loop never ends | Yes, or TypeError |
| Changes lastIndex | Yes | No (it works on a copy) |
| Read it again | Call again any time | No: an iterator is used up after one pass |
The lastIndex difference is the dangerous one. A g regex that exec() or test() has used remembers where it stopped, even when you give it a different string:
const re = /\d/g;
re.exec("a1b2c3"); // finds "1"; lastIndex is now 2
console.log(re.exec("9")); // starts at position 2 of "9": nothing there
matchAll() leaves the regex alone, but its iterator can only be read once, like every iterator (JS Iterators):
const re2 = /\d/g;
const found = "a1b2".matchAll(re2);
console.log([...found].map(m => m[0]));
console.log([...found].length); // already used up
console.log(re2.lastIndex); // untouched
Because matchAll() returns an iterator, the iterator helpers from JS Iterators work on it directly:
console.log("ava=92, ben=85".matchAll(/(\w+)=(\d+)/g).map(m => m[1]).toArray());
matchAll() in new code. You will meet the exec() loop mostly in older code, or with the y flag for step-by-step tokenizers (RegExp Flags). If you do reuse a g regex, reset it first with re.lastIndex = 0.
Instead of a replacement string, pass a function. It is called for every match, and whatever it returns is used as the replacement. It receives the full match first, then each group:
// Double every number
"3 apples and 12 pears".replace(/\d+/g, n => n * 2);
// "6 apples and 24 pears"
// Convert temperatures, using a group
"Mon 20C, Tue 25C".replace(/(\d+)C/g, (match, c) => `${Math.round(c * 9 / 5 + 32)}F`);
// "Mon 68F, Tue 77F"
// camelCase to kebab-case
"backgroundColor".replace(/[A-Z]/g, c => "-" + c.toLowerCase());
// "background-color"
| $& | The whole match |
| $1, $2 ... | Numbered group |
| $<name> | Named group |
| $$ | A literal dollar sign |
"price: 20".replace(/\d+/, "$$$&"); // "price: $20"
"a-b-c".replaceAll(/-/g, "+"); // "a+b+c"
"a-b-c".replaceAll(/-/, "+"); // TypeError: String.prototype.replaceAll called with a non-global RegExp argument
If the split pattern has a capturing group, the separators are kept in the result:
"3+4-2".split(/[+-]/); // ["3", "4", "2"]
"3+4-2".split(/([+-])/); // ["3", "+", "4", "-", "2"] handy for calculators
Every property and method a regex has. Most were covered in RegExp Flags and RegExp Objects:
| compile() | Replaces the pattern in place (deprecated, see below) |
| constructor | The function that made it: RegExp |
| dotAll | true if the s flag is set (ES2018) |
| exec() | One match array, or null |
| flags | All flags as a string, like "gi" (ES2015) |
| global | true if g is set |
| hasIndices | true if d is set (ES2022) |
| ignoreCase | true if i is set |
| lastIndex | Where the next g or y search starts (the only writable one) |
| multiline | true if m is set |
| source | The pattern text |
| sticky | true if y is set (ES2015) |
| test() | true or false |
| toString() | The regex as text: "/pattern/flags" |
| unicode | true if u is set (ES2015) |
| unicodeSets | true if v is set (ES2024) |
| RegExp.escape() | Belongs to RegExp itself, not to each regex: escapes text for a pattern (ES2025) |
const re = /ab+c/gi;
console.log(re.toString());
console.log(re.constructor === RegExp, re.constructor.name);
console.log(/a.b/s.dotAll, /x/d.hasIndices, /x/v.unicodeSets);
console.log(new RegExp("a", "ygmid").flags); // always listed in alphabetical order
compile() is a leftover from very old JavaScript. It rewrites an existing regex in place: after re.compile("b", "i"), a regex that was /a/g is now /b/i. It is deprecated because it changes a regex that other code may be sharing. Make a new one instead: re = new RegExp("b", "i").
unicode (the u flag) "new in 2018." u arrived in ES2015.unicodeSets "new in 2023," while its Patterns page dates the same v flag 2025. It is ES2024.exec() "Returns an Iterator of results." It returns one match array or null, as shown above.test (without the g flag)match without gmatch with g; need their groups too? matchAllreplace with a string, or with a function for logicWhich of these returns an iterator?
str.match(re)re.exec(str)str.matchAll(re)re.test(str)match returns an array or null, exec returns one match array or null, and test returns a boolean.What does this log?
console.log("Tom 3, Ann 5".replace(/\d+/g, n => n * 10));
Tom 30, Ann 50. The function runs once per match; "3" * 10 converts the string to a number, and the result is turned back into text.Why does "a1".matchAll(/\d/) throw an error?
g flag. matchAll always requires it: TypeError: String.prototype.matchAll called with a non-global RegExp argument.match returns null when nothing matches. Fix it with ?? []matchAll with named groups to turn "ava:16, ben:17" into an array of objectsreplace with a function to convert every "Xkm" in a sentence to miles"12*3+4" into numbers and operators, keeping the operators