Standard Library: Regular Expressions

This section documents modern ECMAScript and core browser JavaScript APIs — it is not tied to any specific framework or library (React, Vue, Angular, etc.). This content was generated with the assistance of AI and should be verified against the current ECMAScript specification and MDN documentation before relying on it in production, since JavaScript language features and browser API support continue to evolve.

This section’s bibliography lists the reference material consulted while preparing these pages.

Regular expressions describe textual patterns and let you search, validate, and rewrite strings without hand-rolling character-by-character parsing logic. JavaScript exposes them through the RegExp class, and both String and RegExp define methods that use them.

Defining a Regular Expression

The most common way to create a regular expression is the literal syntax — a pattern between a pair of slashes, optionally followed by one or more flags:

let pattern = /s$/;      // matches any string ending in "s"
let ci = /s$/i;          // same, but case-insensitive

The equivalent RegExp() constructor form is useful when the pattern is built dynamically at runtime (for example, from user input), since a literal can’t embed a variable:

let pattern = new RegExp("s$");
let fromInput = new RegExp(userSearchTerm, "gi");

Character Classes

Most letters and digits match themselves literally. A handful of punctuation characters — ^ $ . * + ? = ! : | \ / ( ) [ ] { } — are metacharacters with special meaning and must be escaped with a backslash (\) to match literally.

Square brackets group literal characters into a character class that matches any one of them; a leading ^ inside the brackets negates the class:

/[abc]/     // matches "a", "b", or "c"
/[^abc]/    // matches any character other than "a", "b", or "c"
/[a-z]/     // any lowercase Latin letter (hyphen = range)

Common classes have dedicated shorthand escapes:

Escape Matches

\w / \W

An ASCII "word" character ([a-zA-Z0-9_]) / anything that isn’t one.

\s / \S

Any Unicode whitespace character / anything that isn’t one.

\d / \D

An ASCII digit ([0-9]) / anything that isn’t one.

\b (inside […​])

A literal backspace character.

The u flag (see Flags) also unlocks Unicode property escapes such as \p{Alphabetic} or \p{Script=Greek}, which match against Unicode character categories rather than the ASCII-only shorthands above.

Quantifiers (Repetition)

Repetition characters apply to whatever immediately precedes them — a single character, a character class, or a parenthesized group:

Quantifier Meaning

?

Zero or one occurrence (optional). Equivalent to {0,1}.

+

One or more occurrences. Equivalent to {1,}.

*

Zero or more occurrences. Equivalent to {0,}.

{n}

Exactly n occurrences.

{n,}

n or more occurrences.

{n,m}

Between n and m occurrences, inclusive.

/\d{2,4}/       // between two and four digits
/\w{3}\d?/      // exactly three word characters, then an optional digit
/\s+java\s+/    // "java" surrounded by one or more spaces

By default, quantifiers are greedy: they consume as much as they can while still letting the rest of the pattern match. Appending ? to a quantifier (??, +?, ?, {1,5}?) makes it non-greedy, matching as little as possible instead. Non-greedy matching only changes *how much is matched once a match is found at a given starting position — it does not change where the engine starts looking, so it can still produce longer matches than you might expect when an earlier starting position is available.

Groups, Alternation & References

Parentheses serve three purposes: grouping a subpattern so a quantifier applies to the whole group, capturing the matched text for later retrieval, and enabling backreferences within the same pattern.

/java(script)?/          // "java" with an optional "script" suffix
/(ab|cd)+/                // one or more repetitions of "ab" or "cd"
/(['"])[^'"]*\1/          // a quoted string whose closing quote matches the opening one

The | character separates alternatives, tried left to right — the first alternative that matches wins, even if a later one would have matched more text.

(?:…​) groups a subpattern without creating a numbered capture, which is useful when you only need the grouping for repetition, not the captured text:

/([Jj]ava(?:[Ss]cript)?)\sis\s(fun\w*)/
// group 1 = the "java"/"Java" + optional "script" part; \2 in a replacement
// still refers to (fun\w*), since the (?:...) group isn't numbered

Named Capture Groups

(?<name>…​) associates a name with a capturing group, which both documents intent and lets you retrieve the match by name instead of by position — via \k<name> for a backreference, or a groups object on the match result (see String Methods That Use Regular Expressions):

let address = /(?<city>\w+) (?<state>[A-Z]{2}) (?<zipcode>\d{5})/;
let quote = /(?<quote>['"])[^'"]*\k<quote>/;   // named backreference

Anchors & Lookaround

Anchors don’t match characters — they match positions:

Anchor Meaning

^

Start of the string (or of a line, with the m flag).

$

End of the string (or of a line, with the m flag).

\b / \B

A word boundary / a position that is not a word boundary.

(?=p) / (?!p)

Positive / negative lookahead — requires (or forbids) p immediately ahead, without consuming it.

(?⇐p) / (?<!p)

Positive / negative lookbehind — requires (or forbids) p immediately behind, without consuming it.

/\bJava\b/                     // "Java" as a whole word, not a prefix like "JavaScript"
/[Jj]ava(?=:)/                 // "Java"/"java", but only when followed by a colon
/(?<=[A-Z]{2} )\d{5}/          // a 5-digit ZIP code, but only after a 2-letter state code

Flags

Flags follow the closing / of a literal (or are passed as the constructor’s second argument) and change how matching behaves, without being part of the pattern itself:

Flag Property Meaning

g

global

Find all matches rather than stopping at the first one; changes how match()/exec() behave (see below).

i

ignoreCase

Case-insensitive matching.

m

multiline

^/$ also match the start/end of each line, not just the whole string.

s

dotAll

. also matches line terminators (normally it doesn’t).

u

unicode

Match full Unicode codepoints instead of 16-bit code units; enables \u{…​} and \p{…​}. Use it by default unless you have a specific reason not to.

y

sticky

Anchor each match attempt to the exact position given by lastIndex, rather than scanning forward for one.

String Methods That Use Regular Expressions

Method Behavior

search(re)

Returns the index of the first match, or -1. Ignores the g flag.

replace(re, replacement)

Replaces the first match, or every match if re has g. replacement can reference captured groups by number ($1, $2, …​) or by name ($<name>), or be a callback function invoked per match.

replaceAll(re, replacement)

Like replace(), but requires the g flag and always replaces every match (throws otherwise).

match(re)

Without g: returns one match array (match[0] is the full match, match[1..] the capture groups, plus .index, .input, and .groups for named captures) or null. With g: returns a plain array of matched substrings only, discarding capture-group detail.

matchAll(re)

Requires g. Returns an iterator of full match objects (the same shape match() returns without g) for every match — the easiest way to loop through all matches with their capture groups intact.

split(re)

Splits the string using the pattern as a delimiter. If the pattern has capturing groups, the captured text is spliced into the returned array between the split pieces.

"7 plus 8 equals 15".match(/\d+/g);                 // => ["7", "8", "15"]

let quote = /"(?<text>[^"]*)"/g;
'He said "stop"'.replace(quote, '«$<text>»');       // => 'He said «stop»'

const words = /\b\p{Alphabetic}+\b/gu;
for (const m of "a naïve test".matchAll(words)) {
  console.log(`Found '${m[0]}' at index ${m.index}.`);
}

"1, 2,\n3".split(/\s*,\s*/);                         // => ["1", "2", "3"]

RegExp Instance Methods

test(str) returns true/false for whether str matches. exec(str) is the lower-level primitive that both test() and the non-global form of match() build on: it always returns a single match array (or null), with the same index/input/groups shape as a non-global match() result.

When a RegExp has the g or y flag set, exec() and test() consult and update the lastIndex property to remember where the next search should resume, instead of always starting from position 0. This makes those flags stateful and error-prone in two opposite ways:

  • Reusing a fresh-literal-per-iteration pattern in a loop condition creates a new RegExp (with lastIndex reset to 0) on every iteration, so a global search never advances — an infinite loop.

  • Reusing the same global/sticky RegExp object across unrelated inputs (e.g. testing several words against one precompiled pattern) leaks lastIndex from one call into the next, silently skipping matches.

// Correct: one RegExp instance, reused deliberately to walk all matches in *one* string.
let pattern = /Java/g;
let text = "JavaScript > Java";
let match;
while ((match = pattern.exec(text)) !== null) {
  console.log(`Matched at ${match.index}; next search resumes at ${pattern.lastIndex}`);
}

Prefer String.matchAll() over a manual exec() loop when you just need every match: it never mutates lastIndex, sidestepping both failure modes above.

See Also