Patterns and flags
A regular expression is a compact pattern for finding structure inside text. Once you learn the notation you can describe things like “a five-digit ZIP code”, “an email address”, or “the word cat but only at the start of a line” and then search, extract, or replace them in one call.
JavaScript exposes this in two connected places: the built-in RegExp object, and a handful of string methods (match, replace, and friends) that accept a RegExp and do the searching for you.
What a regular expression is made of
A regular expression — “regexp”, or just “reg” for short — has two parts: a pattern and an optional set of flags. The pattern describes what to look for. The flags tweak how the search runs.
There are two ways to create one.
The long form calls the constructor with strings:
regexp = new RegExp("pattern", "flags");
The short form wraps the pattern in slashes:
regexp = /pattern/; // no flags
regexp = /pattern/gmi; // with flags g, m and i (covered soon)
The slashes /.../ are what tell JavaScript “this is a regexp”, the same way quotes tell it “this is a string”. Either way, the result is an instance of the built-in RegExp class.
alert( /pattern/ instanceof RegExp ); // true
When to use which syntax
The real difference is dynamism. A slash literal is fixed at the moment you write it — there’s no way to splice a variable into it, the way ${...} works inside a template string. So you reach for slashes when the pattern is known while typing the code, which is most of the time.
new RegExp earns its keep when you build the pattern from a string at runtime:
let tag = prompt("What tag do you want to find?", "h2");
let regexp = new RegExp(`<${tag}>`); // becomes /<h2>/ if the user answered "h2"
Flags
Flags are single letters that change the search’s behavior. JavaScript has exactly six of them.
Here’s what each one does:
pattern:i— the search becomes case-insensitive: no difference betweenAanda(there’s an example just below).pattern:g— global search: the regexp looks for all matches. Without it, only the first match comes back.pattern:m— multiline mode, so the anchors^and$match at the start and end of each line. Covered in Multiline mode of anchors ^ $, flag “m”.pattern:s— “dotall” mode, which lets a dotpattern:.also match a newline\n. Covered in Character classes.pattern:u— full Unicode support, including correct processing of surrogate pairs. Covered in Unicode: flag “u” and class \p{…}.pattern:y— “sticky” mode: match only at one exact position in the text. Covered in Sticky flag “y”, searching at position.
You can combine flags freely and in any order — /.../gi, /.../ig, and new RegExp("...", "gi") all mean the same thing.
Searching: str.match
Regular expressions are wired into the string methods. The first one to know is str.match(regexp), which finds matches of regexp inside str.
Its return value depends on the pattern:g flag and on whether anything matched. Three cases:
1. With the pattern:g flag it returns an array of all matches:
let str = "Waves rise, WAVES fall";
alert( str.match(/waves/gi) ); // Waves,WAVES (an array of 2 substrings that match)
Both match:Waves and match:WAVES show up because the pattern:i flag ignores case.
2. Without pattern:g it returns only the first match, wrapped in an array. Index 0 holds the full match, and the array also carries a few extra properties:
let str = "Waves rise, WAVES fall";
let result = str.match(/waves/i); // without flag g
alert( result[0] ); // Waves (the first match)
alert( result.length ); // 1
// Details:
alert( result.index ); // 0 (position of the match in the string)
alert( result.input ); // Waves rise, WAVES fall (the source string)
The array can hold more indexes than just 0 when part of the pattern is wrapped in parentheses. Those are capturing groups, and they get their own chapter: Capturing groups.
3. When nothing matches, you get null — not an empty array. This holds whether or not pattern:g is set, and it trips people up constantly:
let matches = "Sunlight".match(/Moon/); // = null
if (!matches.length) { // Error: Cannot read properties of null (reading 'length')
alert("Error in the line above");
}
The fix is a fallback to an empty array, so downstream code always has something array-shaped to work with:
let matches = "Sunlight".match(/Moon/) || [];
if (!matches.length) {
alert("No matches"); // now it works
}
Toggle the pattern:g and pattern:i flags below and watch how the shape of the return value changes — an array of every match, a single-match array, or null:
Replacing: str.replace
str.replace(regexp, replacement) swaps out matches of regexp for the replacement. The pattern:g flag decides the scope: with it, every match is replaced; without it, only the first.
// no flag g — only the first "waves" changes
alert( "Waves rise, waves fall".replace(/waves/i, "Tides") ); // Tides rise, waves fall
// with flag g — both change
alert( "Waves rise, waves fall".replace(/waves/ig, "Tides") ); // Tides rise, Tides fall
The replacement string isn’t purely literal. Certain $-sequences act as placeholders that get filled in from the match:
| Symbols | Action in the replacement string |
|---|---|
$& |
inserts the whole match |
$` |
inserts the part of the string before the match |
$' |
inserts the part of the string after the match |
$n |
if n is a 1-2 digit number, inserts the contents of the n-th parentheses — see Capturing groups |
$<name> |
inserts the contents of the named parentheses name — see Capturing groups |
$$ |
inserts a literal $ |
Here pattern:$& reinserts whatever matched, so we can wrap text around it:
alert( "I use CSS".replace(/CSS/, "$& and Sass") ); // I use CSS and Sass
Edit the replacement string below and see the $-placeholders fill themselves in. Try [$&], or $ followed by a backtick and $' to grab the text on either side of the match:
Testing: regexp.test
Sometimes you don’t care what matched, only whether something did. regexp.test(str) looks for at least one match and returns a boolean.
let str = "I enjoy TypeScript";
let regexp = /ENJOY/i;
alert( regexp.test(str) ); // true
The pattern:i flag is why ENJOY finds enjoy. test is cheaper to write than match when all you need is a yes/no, for example in an if.
Type in the box below: the badge updates live as test reports whether the pattern is found. The default pattern \d asks “does the text contain a digit?” — change the pattern to explore:
There’s much more to regular expressions — the pattern language itself, more examples, and more methods — coming up through the rest of this part. The full reference for the methods is in Methods of RegExp and String.
Summary
- A regular expression is a pattern plus optional flags:
pattern:g,pattern:i,pattern:m,pattern:u,pattern:s,pattern:y. - Write it with slashes (
/.../) when the pattern is fixed, or withnew RegExp(str)when you need to build it from a string at runtime. - With no flags and no special symbols, searching by a regexp behaves just like a plain substring search.
str.match(regexp)finds matches: all of them with thepattern:gflag, otherwise just the first — andnullwhen there are none.str.replace(regexp, replacement)replaces matches: all withpattern:g, otherwise only the first. Thereplacementcan use$-placeholders to weave in parts of the match.regexp.test(str)returnstruewhen there’s at least one match, otherwisefalse.