Modern regular expressions
Regex grew up.
Named groups, matchAll, lookbehind, the d flag, and callback replacements turn regex from write-only line noise into readable, structured parsing.
Name your captures
Numbered groups (match[1], match[2]) are positional and fragile β insert a group and every index shifts. Named capture groups (?<year>\d{4}) land in match.groups.year, so your parsing code reads like the data it extracts and survives pattern edits.
Iterate every match with matchAll
String.prototype.matchAll returns an iterator of full match objects β each with its index, capture groups, and named groups. Unlike String.replace with a global flag, you get the whole match shape, and unlike regex.exec in a loop you don't manage lastIndex by hand. The regex must carry the g flag.
Look around without consuming
Lookbehind (?<=...) and lookahead (?=...) assert that text exists before or after the cursor without including it in the match. That makes them perfect for "the number after $" or "the digits before kg" β you match the payload, not the marker.
The d flag: where, not just what
The d (hasIndices) flag adds a .indices array to each match giving [start, end] offsets for the whole match and every group β including named ones under indices.groups. Use it when you need to highlight or splice the source text, not just read the captured value.
Callbacks and capturing splits
replace/replaceAll accept a function: it receives the match, each group, the offset, and the source, and returns the replacement β arbitrary logic per match, no template-string limits. And split with a regex that captures keeps the delimiters in the output array, so you can tokenise while preserving what you split on.
Try it 5 examples
Named capture groups
JSintrom.groups yields { year, month, day } so y=2026 m=06 d=19 reads straight from the names, and the $<name> syntax reorders the parts into 19/06/2026 without a single numeric index.