· Zerdalu
Go Back

Regular Expressions in JavaScript

Regular expressions (regex) are often viewed as a cryptic “black box” by developers. While a complex pattern might look like a cat walked across your keyboard, regex is actually one of the most powerful tools in a programmer’s arsenal. It is the definitive way to search, edit, and manipulate text at scale.

In JavaScript, regex is a first-class citizen. Whether you are validating a form, parsing log files, or cleaning up user input, understanding how to write efficient patterns will save you hundreds of lines of imperative code.


1. Introduction to Regular Expressions

At its core, a regular expression is a sequence of characters that forms a search pattern.

Why it matters: Regex allows you to perform complex string logic that would be nearly impossible with standard string methods like .indexOf() or .includes().

Common use cases:

  • Validation: Ensuring an input looks like an email or a phone number.
  • Parsing: Extracting specific data from a large block of text.
  • Search and Replace: Reformatting dates or stripping HTML tags.

When NOT to use regex: If a standard string method (like .startsWith()) can do the job, use it. Regex comes with a performance overhead and a readability cost. Don’t use regex to parse complex, nested structures like HTML or JSON—use a dedicated parser instead.


2. Regex Basics (JavaScript Syntax)

In JavaScript, you can create a regular expression in two ways:

Literal Syntax

The most common way. The pattern is enclosed between two forward slashes. This is compiled when the script is loaded.

const pattern = /zerdalu/i;

Constructor Function

Useful when the pattern is dynamic (e.g., based on user input).

const word = "zerdalu";
const pattern = new RegExp(word, "i");

Flags

Flags change how the pattern is interpreted:

  • g (global): Find all matches rather than stopping after the first.
  • i (ignore case): Case-insensitive matching.
  • m (multiline): ^ and $ match start/end of lines, not just the whole string.
  • u (unicode): Enables full Unicode support.
  • s (dotAll): Allows . to match newline characters.

3. Character Matching

Patterns are built using a mix of literal characters and special symbols.

  • Literal Characters: /abc/ matches exactly “abc”.
  • Metacharacters:
    • . matches any character except newlines.
    • ^ matches the start of the string.
    • $ matches the end of the string.
  • Character Classes:
    • [abc] matches ‘a’, ‘b’, or ‘c’.
    • [a-z] matches any lowercase letter.
    • [^0-9] matches any character that is not a digit.
  • Predefined Classes:
    • \d: Any digit (same as [0-9]).
    • \w: Any word character (alphanumeric + underscore).
    • \s: Any whitespace (space, tab, newline).
    • \D, \W, \S: The opposites (non-digit, non-word, non-whitespace).

Examples

// Character Classes: [abc] matches any of the characters
const colorRegex = /[rb]ed/;
colorRegex.test("red"); // true
colorRegex.test("bed"); // true
colorRegex.test("fed"); // false

// Ranges: [a-z] matches any lowercase letter
const lowercase = /[a-z]/;
lowercase.test("A"); // false

// Negated Classes: [^0-9] matches anything that is NOT a digit
const noDigits = /[^0-9]/;
noDigits.test("7"); // false
noDigits.test("a"); // true

4. Quantifiers

Quantifiers define how many times a character or group should repeat.

  • *: 0 or more times.
  • +: 1 or more times.
  • ?: 0 or 1 time (optional).
  • {n}: Exactly n times.
  • {n,}: n or more times.
  • {n,m}: Between n and m times.

Greedy vs. Lazy Matching

By default, regex is greedy—it matches as much as possible. Example: /<.*>/ on <div>content</div> will match the whole string. Adding a ? makes it lazy: /<.*?>/ will match only <div>.

Examples

// Optional: Match 'color' (US) or 'colour' (UK)
const spelling = /colou?r/;
spelling.test("color"); // true
spelling.test("colour"); // true

// Ranges: Match a specific length (e.g., a 5-digit ZIP code)
const zipRegex = /^\d{5}$/;
zipRegex.test("12345"); // true
zipRegex.test("123"); // false

// Greedy vs. Lazy
const text = "<div>Hello</div>";

// Greedy: Matches as much as possible
text.match(/<.*>/)[0]; // "<div>Hello</div>"

// Lazy: Matches the smallest possible chunk
text.match(/<.*?>/)[0]; // "<div>"

5. Anchors and Boundaries

Anchors don’t match characters; they match positions.

  • ^: Start of the string (or line in multiline mode).
  • $: End of the string.
  • \b: A word boundary (the position between a word character and a non-word character).
  • \B: A non-word boundary.
const regex = /\bcat\b/;
"cat".match(regex); // Match
"category".match(regex); // No Match

6. Groups and Capturing

Groups allow you to treat multiple characters as a single unit using ( ).

  • Capturing Groups: (abc) matches “abc” and remembers the match for later use.
  • Non-capturing Groups: (?:abc) matches but doesn’t “save” the result (better for performance).
  • Named Groups (ES2018): (?<id>\d+) allows you to access the match via groups.id instead of an index.
const regex = /(?<year>\d{4})-(?<month>\d{2})/;
const result = regex.exec("2026-04");
console.log(result.groups.year); // "2026"

7. Alternation and Logic

The pipe | symbol acts as an OR operator.

const fruits = /apple|orange|banana/;

You can combine this with groups for complex logic: /(red|green) apple/ matches “red apple” or “green apple”.


8. Lookaheads and Lookbehinds

Lookarounds allow you to match a pattern only if it is (or isn’t) followed or preceded by another pattern, without including that second pattern in the result.

  • Positive Lookahead: (?=...) — Match if followed by…
  • Negative Lookahead: (?!...) — Match if NOT followed by…
  • Positive Lookbehind: (?<=...) — Match if preceded by…
  • Negative Lookbehind: (?<!...) — Match if NOT preceded by…

Note: While lookbehinds are now widely supported in modern browsers and Node.js, always check compatibility if you are targeting older environments like Internet Explorer (which does not support them).

Positive Lookahead (?=...)

Match a value only if it is followed by a specific string.

// Match numbers only if they are followed by 'px'
const sizeRegex = /\d+(?=px)/;
"20px".match(sizeRegex); // ["20"]
"20em".match(sizeRegex); // null

Negative Lookahead (?!...)

Ensure a string is NOT followed by something.

// Match 'Java' only if it is NOT followed by 'Script'
const langRegex = /Java(?!Script)/;
langRegex.test("JavaScript"); // false
langRegex.test("Java"); // true

Positive Lookbehind (?<=...)

Match a value only if it is preceded by a specific string.

// Match the price value following a dollar sign
const priceRegex = /(?<=\$)\d+/;
"$100".match(priceRegex); // ["100"]
"€100".match(priceRegex); // null

Negative Lookbehind (?<!...)

Ensure a string is NOT preceded by something.

// Match 'red' only if it is not preceded by 'dark '
const colorRegex = /(?<!dark )red/;
colorRegex.test("dark red"); // false
colorRegex.test("bright red"); // true

9. Working with Regex in JavaScript

RegExp Methods

  • regex.test(str): Returns true or false. (Fastest way to validate).
  • regex.exec(str): Returns an array of match info or null.

String Methods

  • str.match(regex): Returns all matches (if g flag is used).
  • str.replace(regex, replacement): Swaps matches for new text.
  • str.search(regex): Returns the index of the first match.
  • str.split(regex): Breaks a string into an array using the regex as a delimiter.

10. Practical Examples

Email Validation (Simplified)

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

Extracting Numbers

const text = "The price is $99.90";
const price = text.match(/\d+(\.\d+)?/)[0]; // "99.90"

Password Strength

Must have at least one uppercase, one lowercase, one number, and 8 characters.

const strongPassword = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;

11. Unicode Property Escapes

With the u flag, JavaScript can match characters based on their Unicode properties. This is vital for non-Latin text and emojis.

// Match any emoji
const emojiRegex = /\p{Emoji}/u;

// Match any Greek letter
const greekRegex = /\p{Script=Greek}/u;

12. Performance: Avoid Catastrophic Backtracking

Regex can be slow if not written carefully. Catastrophic backtracking occurs when a pattern has nested quantifiers (like (a+)+$) and is given a string that almost matches but fails at the end. The engine tries every possible combination, leading to a “Regex Denial of Service” (ReDoS).

Rule of thumb: Avoid nesting + or * inside each other.


13. Readability and Maintainability

Regex is notoriously hard to read. To keep your code maintainable:

  1. Break it down: Store parts of complex regex in variables.
  2. Comment: Use comments to explain what specific groups are doing.
  3. Named Groups: Use (?<name>...) so future developers (including you) know what the data represents.

14. Conclusion

Mastering regular expressions is not about memorizing every symbol; it’s about knowing what is possible and how to look it up. Start with simple validation, use tools like RegExr or Regex101 to test your patterns, and always consider the performance implications of your quantifiers.

With these patterns in your pocket, you no longer just “process strings”—you manipulate them with surgical precision.