iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

JS RegExp

Regex in JavaScript: literals, the RegExp constructor, flags, capture groups, lookarounds, named captures, replaceAll, and matchAll.

Regex — JavaScript essentials

EXAMPLE
// ===== Two ways to make a regex =====
const lit = /\bworld\b/gi;                       // literal, fastest, compiled once
const dyn = new RegExp('\\bworld\\b', 'gi');  // dynamic, double-escaped

// ===== Flags =====
// g  global       (use across whole string; required for replaceAll / matchAll on regex)
// i  ignore case
// m  multiline (^ and $ match per line)
// s  dotall (. matches \n)
// u  Unicode (\p{...}, \u{...}, \w covers Unicode letters)
// y  sticky (must match at lastIndex)
// d  hasIndices (provides match.indices)

// ===== Test and match =====
/cat/.test('catwalk');                  // true (boolean)
'catwalk'.match(/cat/);                 // ['cat', index: 0, input: ...]
'catwalk dogwalk'.match(/walk/g);       // ['walk', 'walk']
[...'catwalk dogwalk'.matchAll(/(\w+)walk/g)];  // each match with capture group

// matchAll yields RegExpMatchArray objects with groups + index.

// ===== Capture groups =====
const m = 'order #42 total $49.95'.match(/order #(\d+) total \$(\d+\.\d{2})/);
m[0];                  // whole match
m[1];                  // '42'
m[2];                  // '49.95'

// Named groups:
const m2 = 'iso 2024-04-10'.match(/(?<y>\d{4})-(?<mo>\d{2})-(?<d>\d{2})/);
m2.groups.y;           // '2024'

// Non-capturing:
const m3 = 'abc123'.match(/(?:abc)(\d+)/);
m3[1];                 // '123'

// ===== Replace =====
'hello WORLD'.replace(/world/i, 'web');           // 'hello web'
'foo foo'.replaceAll('foo', 'bar');               // 'bar bar' (string mode)
'hello WORLD'.replaceAll(/world/gi, 'web');       // requires g flag with RegExp

// Replace with a function:
'$49.95 and $3.10'.replace(/\$(\d+)\.(\d{2})/g, (_, dollars, cents) => {
  return \`${(+dollars * 100 + +cents)} cents\`;
});

// Named backref in replacement:
'2024-04-10'.replace(/(?<y>\d{4})-(?<mo>\d{2})-(?<d>\d{2})/, '$<d>/$<mo>/$<y>');
// '10/04/2024'

// ===== Lookarounds =====
// (?=...) positive lookahead, (?!...) negative lookahead
// (?<=...) positive lookbehind, (?<!...) negative lookbehind
'price: $49'.match(/(?<=\$)\d+/);              // ['49'] (lookbehind for $)
'pickAB pickXY'.match(/pick(?=A)/g);              // ['pick']
'pickAB pickXY'.match(/pick(?!A)/g);              // ['pick']

// ===== Unicode =====
'café'.match(/\w+/u);                            // ['café'] (with /u)
'café'.match(/\w+/);                              // ['caf']  (without /u)
'abc 中文'.match(/\p{L}+/gu);                     // ['abc', '中文']

// ===== exec for multi-pass with lastIndex =====
const re = /\d+/g;
let mm;
while ((mm = re.exec('a1 b22 c333')) !== null) {
  console.log(mm[0], mm.index);
}

// ===== Common patterns =====
// Email-ish     /^[^\s@]+@[^\s@]+\.[^\s@]+$/
// URL host      /^https?:\/\/([^/]+)/
// ISO date      /^\d{4}-\d{2}-\d{2}$/
// Hex colour    /^#([0-9a-f]{3}|[0-9a-f]{6})$/i

// ===== Pitfalls =====
// - Forgetting /g with replaceAll on a RegExp -> throws
// - Building dynamic regex from user input without escaping -> ReDoS / injection
// - .* across newlines without /s -> 'why doesn't it match?'
// - Catastrophic backtracking: (a+)+$ on 'aaaa...X' is exponential
// - Mixing test() and global flags -> shares lastIndex, surprising behaviour on second call
// - Lookbehind on older Safari/iOS: check support before pinning it in critical code

// ===== Patterns to internalise =====
// - Anchor with ^ and $ when matching the whole string
// - Prefer named groups when more than two captures appear
// - matchAll for iteration; .match(/g) only gives strings, no indices
// - Escape user input that becomes a regex (helper: s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))

Why it matters

JavaScript regex is mostly the standard set with /u, /d, named groups, and matchAll on top. Compile literals where possible, escape dynamic patterns, anchor strictly, and reach for lookarounds when you need context-sensitive matches. The flags + named groups + matchAll combo covers nearly every quick text job without reaching for a parser.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
const re = /(?<area>\d{3})-(?<num>\d{4})/;
const m = '555-867-5309'.match(re);
console.log(m?.groups?.area, m?.groups?.num);
Try it Yourself »

Discussion

Loading…