What the flags do
| Flag | Name | Effect |
|---|---|---|
g |
global | Find every match rather than stopping at the first |
i |
ignore case | Cat matches cat |
m |
multiline | ^ and $ match at the start and end of each line |
s |
dotall | . also matches a newline |
u |
unicode | \u{1F600} works, and astral characters count as one |
y |
sticky | Match only at the exact position where the last one ended |
m and s are the pair people mix up: m changes what the anchors mean, s changes what the dot means.
Greedy, lazy, and why .* eats too much
Quantifiers are greedy by default: they take as much as they can and give back only when the rest of the pattern fails.
Against <b>one</b> <b>two</b>:
<b>.*</b>matches the whole line, because.*runs to the end and backtracks only to the final</b>.<b>.*?</b>matches just<b>one</b>. The?makes the quantifier lazy: take as little as possible.<b>[^<]*</b>is better still: it can’t cross a<at all, so there’s no backtracking to do.
The last form is the one to reach for. Being specific about what a wildcard may not contain is usually both clearer and faster than making it lazy.
Groups
(...)captures, and is numbered from left to right by its opening bracket.(?<name>...)captures with a name, which is easier to read and survives a pattern being edited.(?:...)groups without capturing: use it when you only need the brackets for alternation or a quantifier.
In a replacement, refer to them as $1 or $<name>. $& is the whole match, and $$ is a literal dollar sign.
Catastrophic backtracking
Some patterns take exponential time on input that almost matches. The classic shape is a quantifier inside a quantifier:
(a+)+$ against aaaaaaaaaaaaaaaaaaaaaaaaaaX
Each extra a doubles the work, so a few dozen characters can lock a tab, a server thread, or both. This tester runs patterns in a background worker and stops any that take more than half a second, so you get a warning rather than a frozen page. It’s the basis of a class of denial-of-service bug.
Signs to watch for: nested quantifiers, alternations that can match the same text in more than one way, and .* repeated in the same pattern. Anchoring the pattern and forbidding the delimiter inside a character class usually removes the problem.
Where JavaScript differs from other flavours
| Feature | JavaScript | Elsewhere |
|---|---|---|
| Named groups | (?<name>…) |
Python also allows (?P<name>…) |
| Lookbehind | Supported | Not in older PCRE builds, or in Go’s RE2 |
| Inline flags | Not supported | (?i) works in PCRE, Python and Go |
| Atomic groups, possessive quantifiers | Not available | PCRE has both |
\d, \w |
ASCII only unless you ask for unicode | Often unicode-aware by default |
If a pattern must run in another language, test it there too: this page is honest about being JavaScript’s engine rather than pretending to be universal.