Back to JavaScript
2026-01-135 min read

Lookahead assertion: (?=...), (?!...) (JavaScript)

Learn Lookahead assertion: (?=...), (?!...) (JavaScript) step by step with clear examples and exercises.

Title: Lookahead Assertion: (?=...), (?!...) - JavaScript

Why This Matters

In JavaScript, regular expressions are a powerful tool for pattern matching and manipulating strings. The lookahead assertions (?= and !?) allow you to check if a certain pattern appears ahead or behind in the string without actually consuming any characters. They can be particularly useful for validating input, ensuring code readability, and preventing common programming errors.

Prerequisites

Before diving into lookahead assertions, it's essential to have a good understanding of:

  1. JavaScript basics (variables, functions, loops, conditional statements)
  2. Regular expressions in JavaScript (syntax, pattern matching, and capturing groups)
  3. Basic string manipulation functions (substring(), replace(), etc.)
  4. Understanding the difference between a match and a capture group
  5. Familiarity with various regular expression modifiers like g, i, and m
  6. Comprehension of character classes, quantifiers, and alternations in regular expressions
  7. Knowledge of capturing groups and named captures
  8. Experience working with positive and negative character assertions (^ and $)

Core Concept

Lookahead assertions consist of two types: positive lookahead (?=) and negative lookahead (?!). Both are zero-width assertions, meaning they do not consume any characters during matching.

Positive Lookahead (?=)

The positive lookahead checks if the pattern follows the current position in the string. If the pattern matches, the regular expression continues to match; otherwise, it fails. Here's an example:

let str = "Hello World!";
let regex = /(?=World)Hello/; // Matches only if 'World' follows 'Hello'
console.log(regex.exec(str)[0]); // Output: 'Hello' (since the match is the first part of the string)

In the above example, the regular expression checks if 'World' follows 'Hello'. Since it does, the match returns 'Hello'. However, the entire matched string includes both 'Hello' and 'World', as the lookahead assertion only ensures that 'World' appears after 'Hello'.

Negative Lookahead (?!)

The negative lookahead checks if the pattern does not follow the current position in the string. If the pattern matches, the regular expression fails; otherwise, it continues to match. Here's an example:

let str = "Hello World!";
let regex = /(?!World)Hello/; // Matches only if 'World' does not follow 'Hello'
console.log(regex.exec(str)); // Output: null (since no match is found)

In this example, the regular expression checks if 'World' does not follow 'Hello'. Since it does (it comes after 'Hello'), the test fails and returns null.

Worked Example

Let's create a simple function that validates an email address using lookahead assertions:

function validateEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; // Basic email validation regex
const lookahead = /\.(?!\.)[@\w-]{2,}\./; // Lookahead assertion to check for invalid TLDs (e.g., .foo.)

return regex.test(email) && lookahead.test(email);
}

let emails = ["john@example.com", "jane@example.co.uk", "invalid1@example.", "invalid2@example..com"];
emails.forEach((email) => console.log(`${email}: ${validateEmail(email)}`));

In this example, we first define a basic email validation regex that checks for a valid domain name (e.g., [^\s@]+@[^\s@]+\.[^\s@]+$). Then, we create a lookahead assertion to check if the TLD does not contain a dot before the extension (e.g., .(?!\.)). Finally, we combine both regexes and validate several email addresses.

Common Mistakes

  1. Forgetting to escape special characters in the lookahead pattern:
let regex = /(?=World)Hell/; // Incorrect: 'Hell' will match any string followed by 'World', not just 'Hello'

To fix this, make sure to escape special characters using a backslash (\):

let regex = /(?=World)Hello/; // Correct: matches only if 'World' follows 'Hello'
  1. Using lookahead assertions inappropriately:

Lookahead assertions can be overused, leading to complex and hard-to-read regular expressions. Use them sparingly and only when necessary for readability and efficiency.

  1. Assuming that lookaheads always consume characters:

Lookahead assertions are zero-width assertions; they do not consume any characters during matching. This means that the current position in the string remains unchanged after a positive or negative lookahead is evaluated.

Subheading: Common Mistakes - Practice Questions

  1. Using capturing groups inside a lookahead assertion:
let regex = /(?=(\d{3}))\d/; // Incorrect: the lookahead will capture '3' digits, not just match them

To fix this, use non-capturing groups ((?:...)):

let regex = /(?=(?:\d{3}))\d/; // Correct: matches a digit after 3 digits without capturing '3'
  1. Using a lookahead assertion to check for the absence of a pattern at any position in the string:
let regex = /^(?!.*badword).*$/; // Incorrect: matches strings containing 'badword', but not the entire string should be 'badword'

To fix this, use a negative lookbehind (?<!):

let regex = /^(?<!badword).*$/; // Correct: matches strings that do not start with 'badword'

Practice Questions

  1. Write a regular expression that matches strings containing at least one uppercase letter, followed by exactly three digits, and ending with an underscore (_). Use a positive lookahead to ensure the underscore is not part of a digit sequence.
let regex = /(?=[A-Z][0-9]{3}_)[A-Za-z0-9_]+/; // Matches strings like "Example_123_456" but not "123_Example_456"
  1. Write a regular expression that validates phone numbers in the format (XXX) XXX-XXXX. Use negative lookaheads to disallow invalid characters (e.g., spaces, dashes, or parentheses) between the area code and exchange.
let regex = /^\(?(?![\s-])[0-9]{3}\)?[-.\s]?[0-9]{3}-[0-9]{4}$/; // Matches phone numbers like "(123) 456-7890" but not "123 (456) - 7890"
  1. Write a regular expression that matches email addresses with a domain name containing exactly two letters followed by a dot (e.g., .com, .co.uk). Use a positive lookahead to ensure the dot is not part of a subdomain or top-level domain.
let regex = /^[^\s@]+@([a-zA-Z]{2,}\.)+[a-zA-Z]{2,}$/; // Matches email addresses like "john@example.com" but not "john@sub.domain.co."

FAQ

What happens if I use a negative lookahead in a pattern that doesn't match?

  • If the negative lookahead fails (i.e., the pattern matches), the regular expression will continue to match.

Can I use both positive and negative lookaheads in the same regular expression?

  • Yes, you can use multiple lookaheads (positive or negative) within a single regular expression.

Are there any limitations to using lookahead assertions in JavaScript?

  • Lookahead assertions can be slower than other parts of a regular expression due to their zero-width nature. Use them sparingly and only when necessary for readability or efficiency.

How do I test if a string matches a regular expression in JavaScript?

  • You can use the test() method, like so: let regex = /pattern/; let str = "test string"; console.log(regex.test(str)); // Outputs true or false

How do I get all matches for a regular expression in JavaScript?

  • You can use the exec() method, which returns an array containing the match and any capture groups: let regex = /pattern/g; let str = "multiple matches"; let matches = []; while (match = regex.exec(str)) { matches.push(match[0]); } console.log(matches); // Outputs all matches as an array

How do I create a named capture group in JavaScript?

  • You can use the (?pattern) syntax to name a capture group: let regex = /(?\d{3})-\d{3}-\d{4}/; // Matches phone numbers with named capture group 'areaCode'
Lookahead assertion: (?=...), (?!...) (JavaScript) | JavaScript | XQA Learn