Named groups. (?<name>…) is a capturing group that is also numbered. The match result gets a groups object (null prototype) with one property per name, or groups: undefined when the pattern has no named group. Reference the group inside the pattern with \k<name>, in a replacement string with $<name>, and in a replacer function through its last argument (the groups object).
Duplicate names (ES2025). The same name may be used again if the two groups can never both participate in a match, which in practice means different alternatives: /(?<y>\d{4})-\d\d|\d\d\/(?<y>\d{4})/. groups.y holds whichever alternative matched. Two groups of one name in sequence is still a SyntaxError. Node.js 24 supports it, as do Chrome 125, Firefox 129 and Safari 17.
Lookaround is a zero-width assertion: it tests the text next to the current position and consumes nothing.
| Syntax | Meaning |
|---|---|
(?=x) |
next text matches x |
(?!x) |
next text does not match x |
(?<=x) |
preceding text matches x |
(?<!x) |
preceding text does not match x |
What interviewers probe:
- Lookbehind is evaluated backwards. The engine matches the lookbehind body right to left, so in
/(?<=(\d+)(\d+))$/on'1053'the second group is greedy and takes'053', leaving'1'for the first. JavaScript allows variable-length lookbehind, unlike some engines. - Lookarounds are atomic. Once the body matches, the engine never backtracks into it, so its captures are fixed. A negative lookaround never leaves captures set.
- Captures inside a lookahead are visible afterwards, which is useful (and the basis of the atomic-group trick in the backtracking question).
- A backreference to a group that did not participate matches the empty string.