Regular expressions are a compact language for describing text patterns. JavaScript has had them since the beginning, and the modern features (named groups, lookaround, Unicode property escapes) make them powerful enough for real production code. This lesson walks through syntax, common patterns, and the methods you will use day to day.
Creating a regular expression
Use a literal for a pattern you write inline. Use the RegExp constructor when you build the pattern from a string at runtime. The flags (g for global, i for case-insensitive, m for multiline) change the matching behaviour.
// Literal with flags
const phone = /\d{3}-\d{4}/g;
// Constructor for a dynamic pattern
const code = "AB";
const pat = new RegExp(`${code}\\d{2,4}`, "i");
const text = "Call 555-1234 or 555-5678.";
console.log(text.match(phone)); // ['555-1234', '555-5678']
The metacharacters you will use most
. matches any character except newline. \d matches a digit, \w a word character, \s whitespace. ^ and $ anchor to the start and end of the string. Use a character class [...] for a set of allowed characters. Repeat with *, +, ?, and {n,m}.
const pat = /^[\w.+-]+@[\w-]+\.[\w.-]+$/;
console.log(pat.test("[email protected]")); // true
console.log(pat.test("[email protected]")); // true
console.log(pat.test("not-an-email")); // false
Groups, capture, and named capture
Parentheses group a sub-pattern and capture the matched text. Numbered groups are accessed by index. Named groups use (?P<name>...) in Python and (?<name>...) in JavaScript. They survive refactors and make replacement strings readable.
const date = "2026-04-15";
const pat = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const m = date.match(pat);
console.log(m.groups.year); // 2026
console.log(m.groups.month); // 04
console.log(m.groups.day); // 15
Replacement with back-references
The replace method accepts a string or function. In the string, $1 to $9 refer to numbered groups, and $<name> refers to named groups. A function replacement is cleaner for complex substitutions.
const text = "Today is 2026-04-15 and tomorrow is 2026-04-16.";
const swapped = text.replace(/(\d{4})-(\d{2})-(\d{2})/g, "$3/$2/$1");
console.log(swapped); // Today is 15/04/2026 and tomorrow is 16/04/2026
const redacted = text.replace(/\d{4}-\d{2}-\d{2}/g, "[DATE]");
console.log(redacted); // Today is [DATE] and tomorrow is [DATE].
// Function replacement
const upper = text.replace(/\b\d{4}\b/g, (m) => m.toUpperCase());
console.log(upper); // Today is 2026-04-15 and tomorrow is 2026-04-16. (no letters, so no change)
Lookaround assertions
Lookaround matches a position without consuming characters. (?=...) is a positive lookahead. (?!...) is a negative lookahead. Lookbehind is (?<=...) for positive and (?<!...) for negative. Useful for matching a value only when it is surrounded by other values.
// Match a number only when followed by "px"
const sizes = "10 12px 14em 16px 18";
console.log(sizes.match(/\d+(?=px)/g)); // ['12', '16']
// Match a word not followed by a comma
const words = "apple, banana cherry, date";
console.log(words.match(/\b\w+\b(?!,)/g)); // ['banana', 'date']
Split and test
String.split accepts a regex when you need a more flexible separator. RegExp.test returns true or false for a match. For repeated checks on the same pattern, compile once with the literal or constructor and reuse.
const csv = "name, age, city";
console.log(csv.split(/,\s*/)); // ['name', 'age', 'city']
const emailPat = /^[\w.+-]+@[\w-]+\.[\w.-]+$/;
console.log(emailPat.test("[email protected]")); // true
Summary
Use regex literals for inline patterns. Use the RegExp constructor for dynamic patterns. Reach for named groups, lookaround, and function replacements for clean real-world code. The next lesson covers object-oriented JavaScript: classes, prototypes, and inheritance.
Unicode property escapes
The u flag unlocks Unicode property escapes, which match characters by their Unicode category. \p{Letter} matches any letter in any script. \p{Script=Greek} matches Greek letters specifically. \p{Emoji} matches emoji code points. These escapes make regex a first-class tool for international text. The v flag (ES2024) extends the syntax further with set notation and intersection of properties.
const greekWord = /\p{Script=Greek}+/u;
console.log(greekWord.test("αθήνα")); // true
const emojiPat = /\p{Emoji}/u;
console.log(emojiPat.test("hi")); // false
const allLetters = /^\p{Letter}+$/u;
console.log(allLetters.test("café")); // true
console.log(allLetters.test("123")); // false
Performance and pitfalls
Catastrophic backtracking is the classic regex bug. A pattern like (a+)+ on a long string that fails to match can take exponential time. Avoid nested quantifiers whenever possible, and test your patterns on realistic inputs. Most engines also expose a non-greedy version of *, +, and ? with the ? suffix: a+? matches as few a's as possible. Reach for non-greedy when you are looking for the shortest match in a longer string, especially with the dot metacharacter.
// Greedy vs non-greedy
const html = "<b>one</b> and <b>two</b>";
console.log(html.match(/<b>.*<\/b>/)); // matches the whole string
console.log(html.match(/<b>.*?<\/b>/g)); // ['<b>one</b>', '<b>two</b>']
Comparison with Python
Python's re module and JavaScript's RegExp cover the same feature set with different surface syntax. Python uses (?P<name>...) for named groups, JavaScript uses (?<name>...). Python has re.compile for pre-compiling a pattern, JavaScript uses the literal or RegExp constructor with the same effect. Python has re.findall and re.finditer; JavaScript uses match with the g flag, which returns all matches in an array. Python's re.sub plays the same role as JavaScript's String.replace. Unicode property escapes work in both, with the same \p{...} syntax. The biggest practical difference is that JavaScript has first-class Unicode flag (u) and now the v flag for set notation, which Python's re module supports via the regex third-party package.