Difficulty: Advanced
Regular expressions (regex) are patterns used to match character combinations in strings. JavaScript supports regex as first-class objects through the RegExp constructor and regex literal syntax. They are indispensable for tasks like form validation, text parsing, search-and-replace operations, and data extraction from strings.
There are two ways to create a regex in JavaScript: the literal syntax /pattern/flags and the constructor new RegExp('pattern', 'flags'). The literal syntax is evaluated at parse time and is preferred when the pattern is known ahead of time. The constructor is useful when the pattern is dynamic - for example, when building a regex from user input. JavaScript supports several flags: g (global - find all matches), i (case-insensitive), m (multiline - ^ and $ match line boundaries), s (dotAll - . matches newlines), u (Unicode), and y (sticky - matches from lastIndex).
Character classes let you match categories of characters. \d matches any digit (0-9), \w matches word characters (letters, digits, underscore), \s matches whitespace, and . matches any character except newline (unless the s flag is set). You can define custom character classes with brackets: [abc] matches a, b, or c, while [^abc] matches anything except those characters. Ranges are supported too: [a-z] matches any lowercase letter.
Quantifiers control how many times a pattern element should repeat. * means zero or more, + means one or more, ? means zero or one, {n} means exactly n times, {n,} means n or more, and {n,m} means between n and m times. By default quantifiers are greedy (match as much as possible); appending ? makes them lazy (match as little as possible).
Groups and capturing are essential for extracting parts of a match. Parentheses () create a capturing group that stores the matched substring. Non-capturing groups (?:) group patterns without capturing. Named groups (?<name>pattern) let you assign meaningful names to captures, making the code self-documenting. Backreferences like \1 refer to previously captured groups within the same pattern.
Lookahead and lookbehind assertions match a position without consuming characters. Positive lookahead (?=pattern) succeeds if pattern matches ahead, negative lookahead (?!pattern) succeeds if it does not. Positive lookbehind (?<=pattern) and negative lookbehind (?<!pattern) work similarly but look behind the current position. These are powerful for complex matching requirements like finding a word only when preceded or followed by specific text.
// Literal syntax
const re1 = /hello/i;
console.log(re1.test('Hello World'));
// Constructor syntax (useful for dynamic patterns)
const searchTerm = 'world';
const re2 = new RegExp(searchTerm, 'gi');
console.log('Hello World'.match(re2));
// Flags overview
const text = 'Line1\nLine2\nLine3';
console.log(text.match(/^Line\d/gm));
// dotAll flag
console.log(/foo.bar/.test('foo\nbar'));
console.log(/foo.bar/s.test('foo\nbar'));
The i flag enables case-insensitive matching. The g flag finds all matches. The m flag makes ^ and $ match at line boundaries. The s flag makes . match newline characters.
const str = 'Phone: 123-456-7890, Email: test@example.com';
// \d+ matches one or more digits
console.log(str.match(/\d+/g));
// \w+ matches word characters
console.log('hello world 123'.match(/\w+/g));
// Custom character class
console.log('gray grey'.match(/gr[ae]y/g));
// Negated character class
console.log('abc123'.match(/[^a-z]+/g));
// Quantifiers: greedy vs lazy
const html = '<b>bold</b> and <i>italic</i>';
console.log(html.match(/<.+>/g)); // greedy
console.log(html.match(/<.+?>/g)); // lazy
Greedy quantifiers match as much text as possible, so <.+> captures everything between the first < and the last >. Lazy quantifiers (adding ?) match as little as possible, giving you individual tags.
// Capturing groups
const dateStr = '2025-03-10';
const match = dateStr.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(`Year: ${match[1]}, Month: ${match[2]}, Day: ${match[3]}`);
// Named groups
const namedMatch = dateStr.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
console.log(namedMatch.groups);
// Backreference - match repeated words
const repeated = 'the the quick brown fox fox';
console.log(repeated.match(/\b(\w+)\s+\1\b/g));
// Non-capturing group
const urls = 'http://a.com https://b.com';
console.log(urls.match(/(?:https?):\/\/\S+/g));
Named groups make regex results self-documenting. Backreference \1 refers to the first captured group, useful for finding duplicated words. Non-capturing groups (?:) group without creating a capture entry.
// Positive lookahead: match digits followed by 'px'
console.log('12px 5em 30px'.match(/\d+(?=px)/g));
// Negative lookahead: match digits NOT followed by 'px'
console.log('12px 5em 30px'.match(/\d+(?!px)\b/g));
// Positive lookbehind: match amount after $
console.log('$100 and €200 and $50'.match(/(?<=\$)\d+/g));
// Email validation pattern
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
console.log(emailRegex.test('user@example.com'));
console.log(emailRegex.test('invalid@.com'));
// String methods with regex
const text = 'Hello World';
console.log(text.search(/world/i));
console.log(text.replace(/world/i, 'JavaScript'));
console.log('a,b,,c'.split(/,+/));
Lookahead and lookbehind assertions match a position without including the looked-at text in the result. They enable precise matching based on surrounding context.
RegExp, Pattern Matching, Flags, Character Classes, Quantifiers, Groups, Lookahead, Lookbehind