โ† Regular Expressions

Lookahead and Flags

~340 words ยท 2 min read

Lookahead and Flags

Lookaheads assert what comes next without consuming it, and flags change how the entire pattern behaves. Together they make regex far more expressive.

Lookahead

A lookahead (?=...) checks that the following text matches a pattern, but the assertion consumes no characters. The match position stays put.

/foo(?=bar)/   matches "foo" only when followed by "bar"
on "foobar": matches "foo"
on "foobaz": no match

Negative Lookahead

Negative lookahead (?!...) succeeds when the following text does NOT match. It is ideal for "must not contain" rules.

/foo(?!bar)/   matches "foo" only when NOT followed by "bar"

Lookarounds are zero-width assertions: they test a condition at a position without eating characters. That is why foo(?=bar) matches just "foo" โ€” the "bar" is verified but left untouched for whatever follows.

Flags

Flags appear after the closing delimiter and modify matching behavior across the whole pattern.

  • g โ€” global: find all matches, not just the first
  • i โ€” case-insensitive
  • m โ€” multiline: ^ and $ match line boundaries
  • s โ€” dotall: . matches newlines too
/cat/gi       all "cat", any case
/^error/m     "error" at the start of any line
/.+/s         matches across newlines

The Global Flag and State

The g flag makes a regex stateful in JavaScript โ€” exec() and lastIndex track position across calls. Reusing such a regex carelessly can skip matches or loop forever.

const re = /\d+/g;
re.exec("a1b2");   // ["1"], lastIndex becomes 2
re.exec("a1b2");   // ["2"]
re.exec("a1b2");   // null, lastIndex resets to 0

Putting It Together

A password rule like "at least 8 characters, containing a digit" combines lookaheads: /^(?=.*\d).{8,}$/. Master lookarounds and flags, and you can express validation logic that would otherwise need many lines of code.