SKU // 94

JavaScript RegExp Methods

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.

// The Complete Set

Method Returns Use for
re.test(str)true / falseValidation
re.exec(str)one detailed match or nullStep-by-step matching
str.search(re)position or -1Where is it?
str.match(re)array or nullFirst match + groups, or all matches with g
str.matchAll(re)iterator of matchesEvery match + groups
str.replace(re, x)new stringSwap text
str.replaceAll(re, x)new stringSame, needs g
str.split(re)arrayBreak text apart

// match() Changes With the g Flag

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
["x=1", "x", "1"] ["x=1", "y=22"] null
Watch out
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.

// matchAll(): Every Match, Every Group

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);
[{item: "Pens", price: 2.5}, {item: "Paper", price: 4.25}, {item: "Tape", price: 1.99}]

matchAll requires the g flag and throws a TypeError without it.

// exec() vs matchAll()

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()
ReturnsOne match array per call, or nullAn iterator of all match arrays
Needs gYes, or a loop never endsYes, or TypeError
Changes lastIndexYesNo (it works on a copy)
Read it againCall again any timeNo: 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
null

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
["1", "2"] 0 0

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());
["ava", "ben"]
Which to Use
Prefer 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.

// replace() With a Function

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"
6 apples and 24 pears Mon 68F, Tue 77F background-color

// Special Replacement Strings

$&The whole match
$1, $2 ...Numbered group
$<name>Named group
$$A literal dollar sign
"price: 20".replace(/\d+/, "$$$&");   // "price: $20"

// replaceAll() Needs g

"a-b-c".replaceAll(/-/g, "+");   // "a+b+c"
"a-b-c".replaceAll(/-/, "+");    // TypeError: String.prototype.replaceAll called with a non-global RegExp argument
a+b+c TypeError: String.prototype.replaceAll called with a non-global RegExp argument

// split() With Groups

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
["3", "4", "2"] ["3", "+", "4", "-", "2"]

// Complete RegExp Reference

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)
constructorThe function that made it: RegExp
dotAlltrue if the s flag is set (ES2018)
exec()One match array, or null
flagsAll flags as a string, like "gi" (ES2015)
globaltrue if g is set
hasIndicestrue if d is set (ES2022)
ignoreCasetrue if i is set
lastIndexWhere the next g or y search starts (the only writable one)
multilinetrue if m is set
sourceThe pattern text
stickytrue if y is set (ES2015)
test()true or false
toString()The regex as text: "/pattern/flags"
unicodetrue if u is set (ES2015)
unicodeSetstrue 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
/ab+c/gi true RegExp true true true dgimy
Not recommended: compile()
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").
W3Schools errors on its Methods page
  • It dates unicode (the u flag) "new in 2018." u arrived in ES2015.
  • It dates unicodeSets "new in 2023," while its Patterns page dates the same v flag 2025. It is ES2024.
  • Its RegExp methods table says exec() "Returns an Iterator of results." It returns one match array or null, as shown above.

// Choosing a Method

  1. Just need yes or no? test (without the g flag)
  2. Need the parts of one match? match without g
  3. Need every match? match with g; need their groups too? matchAll
  4. Changing text? replace with a string, or with a function for logic

// Quick Check

Which of these returns an iterator?

  1. str.match(re)
  2. re.exec(str)
  3. str.matchAll(re)
  4. re.test(str)
Show answer
C. 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));
Show answer
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?

Show answer
The regex has no g flag. matchAll always requires it: TypeError: String.prototype.matchAll called with a non-global RegExp argument.

// Try It

  1. Click Open Playground and run it: match returns null when nothing matches. Fix it with ?? []
  2. Use matchAll with named groups to turn "ava:16, ben:17" into an array of objects
  3. Use replace with a function to convert every "Xkm" in a sentence to miles
  4. Split a math expression like "12*3+4" into numbers and operators, keeping the operators
// Video Walkthroughs
Last updated: Oct 10, 2026, 1:47 AM ET