|

JavaScript 49 🧬 Regular expressions

const re = /hello/;
console.log(re.test('hello world'));

const re2 = new RegExp('hello');
console.log(re2.test('hello world'));

console.log(/world/.test('hello world'));

console.log(/hello/i.test('HELLO'));

console.log('hello world'.match(/\w+/g));

console.log('2024-01-15'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1'));

console.log('a,b,c'.split(','));

console.log('abc123'.match(/\d+/)[0]);

console.log([...'abc123def456'.matchAll(/\d+/g)].map(m => m[0]));

const pattern = /(\w+)@(\w+)\.com/;
const result = pattern.exec('contact: alice@example.com');
console.log(result[1], result[2]);

console.log(/^\d{4}-\d{2}-\d{2}$/.test('2024-01-15'));

console.log('phone: 555-1234'.replace(/\d{3}-\d{4}/, '***-****'));

console.log('Hello World'.replace(/\s/g, '_'));

Regular expressions (regex) are patterns for matching text. They look cryptic at first — /^\d{3}-\d{4}$/ — but they’re the most powerful way to search, validate, and transform strings. Every language has them, and JavaScript’s are among the most capable.

Key point: A regular expression is an object that describes a pattern. You can create one with a literal (/pattern/flags) or the RegExp constructor (new RegExp('pattern', 'flags')). Use literals when the pattern is fixed, the constructor when it’s built dynamically.


a – What is a regular expression

A regular expression is a pattern used to match character combinations in strings. It’s a small language inside JavaScript with its own syntax.

Two ways to create a regex:

Literal notation (recommended for static patterns):

const re = /hello/;

Constructor (for dynamic patterns):

const re = new RegExp('hello');

Both are equivalent. Literals are compiled once, are faster, and are easier to read.

The basic methods:

MethodOnReturns
regex.test(str)RegexBoolean
regex.exec(str)RegexMatch array or null
str.match(regex)StringMatch array or null
str.matchAll(regex)StringIterator of matches
str.replace(regex, repl)StringNew string
str.replaceAll(regex, repl)StringNew string (all)
str.search(regex)StringIndex or -1
str.split(regex)StringArray

Testing with test:

console.log(/hello/.test('hello world'));
// [ true ]

console.log(/hello/.test('goodbye'));
// [ false ]

Returns true or false — the simplest use.

Finding with match:

console.log('hello world'.match(/world/));
// [ [ 'world', index: 6, input: 'hello world', groups: undefined ] ]

The array has the matched text plus index and input.

Global matches:

console.log('a1 b2 c3'.match(/\w\d/g));
// [ [ 'a1', 'b2', 'c3' ] ]

With the g flag, match returns all matches as a plain array.

Replacing with replace:

console.log('hello world'.replace(/world/, 'there'));
// [ 'hello there' ]

Extracting with exec:

const re = /(\d+)/;
const result = re.exec('abc123def');
console.log(result[0]);
// [ '123' ]
console.log(result[1]);
// [ '123' ]  ← first capture group
console.log(result.index);
// [ 3 ]

exec gives you the full match info — text, index, input, and captures.

Regex flags:

FlagMeaning
gGlobal — find all matches
iCase-insensitive
mMultiline — ^ and $ match line boundaries
sDotall — . matches newlines too
uUnicode — full Unicode support
ySticky — match at lastIndex only
dIndices — add indices property

Examples with flags:

// Case-insensitive
console.log(/hello/i.test('HELLO'));
// [ true ]

// Global — find all
console.log('a a a'.match(/a/g));
// [ [ 'a', 'a', 'a' ] ]

// Multiline
const text = 'line1\nline2\nline3';
console.log(text.match(/^line/gm));
// [ [ 'line', 'line', 'line' ] ]

// Dotall
console.log(/a.b/s.test('a\nb'));
// [ true ]

Why regex matters:

  • Validation — emails, URLs, dates, phone numbers
  • Extraction — pull data out of text
  • Transformation — rename, reformat, sanitize
  • Search — find patterns, not just literal strings
  • Parsing — tokenize simple grammars

Regex is a language:

┌──────────────────────────────────────────────┐
│  /pattern/flags                              │
│   │                                          │
│   │  ┌─────────────────────────────────┐     │
│   │  │ Literal characters: a, b, 1     │     │
│   │  │ Character classes: [a-z], \d    │     │
│   │  │ Quantifiers: *, +, ?, {n,m}     │     │
│   │  │ Anchors: ^, $, \b               │     │
│   │  │ Groups: ( ), (?: )              │     │
│   │  │ Alternation: |                  │     │
│   │  │ Lookaround: (?= ), (?! )        │     │
│   │  └─────────────────────────────────┘     │
│   │                                          │
│   └── flags: g, i, m, s, u, y, d             │
│                                              │
└──────────────────────────────────────────────┘

b – Regex syntax

Regex has a compact syntax for describing patterns. This section covers the essentials.

Literal characters:

console.log(/cat/.test('the cat sat'));
// [ true ]

Most characters match themselves literally.

Metacharacters — special meaning:

CharacterMeaning
.Any character (except newline by default)
^Start of string (or line with m)
$End of string (or line with m)
*0 or more
+1 or more
?0 or 1 (or lazy quantifier)
{n}Exactly n
{n,}n or more
{n,m}Between n and m
[ ]Character class
[^ ]Negated class
( )Group
(?: )Non-capturing group
|Alternation (OR)
\Escape

To match a literal metacharacter, escape it:

console.log(/\./.test('a.b'));
// [ true ]

console.log(/\+/.test('a+b'));
// [ true ]

Character classes:

ClassMatches
\dDigit [0-9]
\DNon-digit [^0-9]
\wWord char [A-Za-z0-9_]
\WNon-word char
\sWhitespace [ \t\n\r\f\v]
\SNon-whitespace
.Any char
console.log(/\d+/.test('abc123'));
// [ true ]

console.log(/\w+/.test('hello_123'));
// [ true ]

console.log(/\s+/.test('a b'));
// [ true ]

Custom character classes:

console.log(/[abc]/.test('cat'));
// [ true ]

console.log(/[a-z]+/.test('hello'));
// [ true ]

console.log(/[A-Z]/.test('Hello'));
// [ true ]

console.log(/[^0-9]/.test('abc'));
// [ true ]

Ranges: [a-z], [A-Z], [0-9], [a-zA-Z0-9_].

Quantifiers:

QuantifierMeaning
*0 or more
+1 or more
?0 or 1
{n}Exactly n
{n,}n or more
{n,m}Between n and m
console.log(/ab*c/.test('ac'));
// [ true ]  ← 0 b's

console.log(/ab*c/.test('abc'));
// [ true ]

console.log(/ab*c/.test('abbbc'));
// [ true ]

console.log(/ab+c/.test('ac'));
// [ false ]  ← needs at least one b

console.log(/ab?c/.test('ac'));
// [ true ]  ← 0 or 1 b

console.log(/a{3}/.test('aaa'));
// [ true ]

console.log(/a{2,4}/.test('aa'));
// [ true ]

Greedy vs lazy quantifiers:

By default, quantifiers are greedy — they match as much as possible.

console.log('<a><b>'.match(/<.+>/)[0]);
// [ '<a><b>' ]  ← greedy: matches to last >

Add ? to make them lazy:

console.log('<a><b>'.match(/<.+?>/)[0]);
// [ '<a>' ]  ← lazy: matches to first >

Anchors:

AnchorMeaning
^Start of string (or line with m)
$End of string (or line with m)
\bWord boundary
\BNon-word boundary
console.log(/^hello/.test('hello world'));
// [ true ]

console.log(/world$/.test('hello world'));
// [ true ]

console.log(/\bcat\b/.test('the cat sat'));
// [ true ]

console.log(/\bcat\b/.test('category'));
// [ false ]  ← cat isn't a whole word

Groups and captures:

const match = '2024-01-15'.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(match[1]);
// [ '2024' ]
console.log(match[2]);
// [ '01' ]
console.log(match[3]);
// [ '15' ]

Parentheses capture matches. Access them by index.

Non-capturing groups:

// (?: ) doesn't capture
const match = 'abc123'.match(/(?:abc)(\d+)/);
console.log(match[1]);
// [ '123' ]

Use (?:...) when you need grouping but not the capture.

Named groups:

const match = '2024-01-15'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
console.log(match.groups.year);
// [ '2024' ]
console.log(match.groups.month);
// [ '01' ]
console.log(match.groups.day);
// [ '15' ]

Named groups make complex patterns readable.

Backreferences:

console.log(/(\w)\1/.test('aa'));
// [ true ]

console.log(/(\w)\1/.test('ab'));
// [ false ]

\1 refers to the first capture group — matches the same text again.

Alternation:

console.log(/cat|dog/.test('I have a dog'));
// [ true ]

console.log(/red|green|blue/.test('yellow'));
// [ false ]

Lookahead and lookbehind:

SyntaxMeaning
(?=...)Positive lookahead
(?!...)Negative lookahead
(?<=...)Positive lookbehind
(?<!...)Negative lookbehind
// Positive lookahead
console.log('price: $100'.match(/\$(?=\d+)/));
// [ [ '$', index: 7, ... ] ]

// Negative lookahead
console.log('foo bar'.match(/foo(?!bar)/));
// [ null ]

// Positive lookbehind
console.log('$100'.match(/(?<=\$)\d+/)[0]);
// [ '100' ]

// Negative lookbehind
console.log('100 dollars'.match(/(?<!\$)\b\d+\b/)[0]);
// [ '100' ]

Lookarounds assert a condition without consuming characters.

Regex cheat sheet:

PatternMatches
\dDigit
\wWord char
\sWhitespace
.Any char
[abc]One of a, b, c
[^abc]Not a, b, c
a*0+ a
a+1+ a
a?0 or 1 a
a{3}Exactly 3
a{2,5}2–5 a
^Start
$End
\bWord boundary
( )Capture
(?: )Non-capture
(?<name> )Named capture
|OR
(?= )Lookahead
(?! )Negative lookahead
(?<= )Lookbehind
(?<! )Negative lookbehind

c – Regex methods and patterns

This section covers the methods and the patterns you’ll use in real code.

test — quick boolean check:

const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
console.log(emailRe.test('alice@example.com'));
// [ true ]
console.log(emailRe.test('not-an-email'));
// [ false ]

Perfect for validation.

exec — full match details:

const re = /(\d{4})-(\d{2})-(\d{2})/;
const match = re.exec('date: 2024-01-15');

console.log(match[0]);   // [ '2024-01-15' ]
console.log(match[1]);   // [ '2024' ]
console.log(match[2]);   // [ '01' ]
console.log(match[3]);   // [ '15' ]
console.log(match.index); // [ 6 ]

exec returns null if no match:

const noMatch = /xyz/.exec('hello');
console.log(noMatch);
// [ null ]

Global exec and lastIndex:

const re = /\d+/g;
const str = 'a1 b2 c3';

let m;
while ((m = re.exec(str)) !== null) {
  console.log(m[0], m.index);
}
// [ '1' 1 ]
// [ '2' 4 ]
// [ '3' 7 ]

With the g flag, exec remembers lastIndex between calls. Loop until null.

match — simpler alternative:

console.log('a1 b2 c3'.match(/\d+/g));
// [ [ '1', '2', '3' ] ]

matchAll — all matches with details:

const re = /(\w)(\d)/g;
const str = 'a1 b2 c3';

for (const m of str.matchAll(re)) {
  console.log(m[1], m[2]);
}
// [ 'a' '1' ]
// [ 'b' '2' ]
// [ 'c' '3' ]

matchAll requires the g flag and returns an iterator.

replace — substitute:

console.log('hello world'.replace(/world/, 'there'));
// [ 'hello there' ]

// Global
console.log('a a a'.replace(/a/g, 'b'));
// [ 'b b b' ]

// With capture groups
console.log('2024-01-15'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1'));
// [ '15/01/2024' ]

// With a callback
console.log('abc'.replace(/./g, ch => ch.toUpperCase()));
// [ 'ABC' ]

Callback receives match, groups, index:

'2024-01-15'.replace(
  /(\d{4})-(\d{2})-(\d{2})/,
  (match, y, m, d) => `${d}.${m}.${y}`
);
// [ '15.01.2024' ]

replaceAll — replace all (string or regex with g):

console.log('a a a'.replaceAll('a', 'b'));
// [ 'b b b' ]

console.log('a a a'.replaceAll(/a/g, 'b'));
// [ 'b b b' ]

search — find index:

console.log('hello world'.search(/world/));
// [ 6 ]

console.log('hello world'.search(/xyz/));
// [ -1 ]

Returns index or -1 — ignores the g flag.

split — split by pattern:

console.log('a1b2c3'.split(/\d/));
// [ [ 'a', 'b', 'c', '' ] ]

console.log('one, two;three'.split(/[,;]\s*/));
// [ [ 'one', 'two', 'three' ] ]

Common regex patterns:

Email (simple):

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

URL:

const urlRe = /^https?:\/\/[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=]*$/;

Date YYYY-MM-DD:

const dateRe = /^\d{4}-\d{2}-\d{2}$/;

Time HH:MM (24h):

const timeRe = /^([01]\d|2[0-3]):[0-5]\d$/;

US Phone:

const phoneRe = /^\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/;

ZIP code (US):

const zipRe = /^\d{5}(-\d{4})?$/;

Hex color:

const hexRe = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;

IPv4:

const ipv4Re = /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;

Username (letters, digits, underscore, 3–16 chars):

const userRe = /^[a-zA-Z0-9_]{3,16}$/;

Strong password:

const pwRe = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/;

HTML tags (rough):

const tagRe = /<[^>]+>/g;

Whitespace:

const wsRe = /\s+/g;

Word boundary:

const wordRe = /\b\w+\b/g;

Extract numbers:

console.log('abc123def456'.match(/\d+/g));
// [ [ '123', '456' ] ]

Extract emails:

const text = 'Contact: alice@a.com or bob@b.org';
const emails = text.match(/[\w.+-]+@[\w.-]+\.\w+/g);
console.log(emails);
// [ [ 'alice@a.com', 'bob@b.org' ] ]

Clean up whitespace:

console.log('  too   much    space  '.replace(/\s+/g, ' ').trim());
// [ 'too much space' ]

Swap first and last name:

console.log('Smith, John'.replace(/(\w+), (\w+)/, '$2 $1'));
// [ 'John Smith' ]

Format number with commas:

function formatNumber(n) {
  return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

console.log(formatNumber(1234567));
// [ '1,234,567' ]

Escape regex special characters:

function escapeRegex(str) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

console.log(escapeRegex('a.b*c'));
// [ 'a\\.b\\*c' ]

Dynamic regex with constructor:

function containsAny(text, words) {
  const pattern = new RegExp(words.map(escapeRegex).join('|'), 'i');
  return pattern.test(text);
}

console.log(containsAny('I love JavaScript', ['python', 'javascript']));
// [ true ]

RegExp lastIndex gotcha:

const re = /\d/g;

console.log(re.test('1'));
// [ true ]

console.log(re.lastIndex);
// [ 1 ]

console.log(re.test('1'));
// [ false ]  ← starts at index 1, no more digits

console.log(re.lastIndex);
// [ 0 ]  ← reset after failed match

With the g flag, test and exec remember where they left off. This is a common bug — either don’t use g with test, or reset lastIndex.

Regex escapes:

EscapeMeaning
\dDigit
\DNon-digit
\wWord char
\WNon-word
\sWhitespace
\SNon-whitespace
\bWord boundary
\BNon-boundary
\nNewline
\tTab
\rCarriage return
\.Literal dot
\*Literal asterisk
\\Literal backslash
\uXXXXUnicode code point
\xHHHex char

Character class shortcuts:

Inside [ ]Meaning
\dDigit
\wWord char
\sWhitespace
.Literal dot (inside class)

Inside [ ], . is literal, but \d, \w, \s still work as shortcuts.


Complete Example Session

// ============================================
// PART 1: BASIC TEST
// ============================================

console.log(/hello/.test('hello world'));
// [ true ]

console.log(/hello/.test('goodbye'));
// [ false ]

// ============================================
// PART 2: CONSTRUCTOR
// ============================================

const re = new RegExp('world');
console.log(re.test('hello world'));
// [ true ]

// ============================================
// PART 3: FLAGS
// ============================================

console.log(/hello/i.test('HELLO'));
// [ true ]

console.log('a a a'.match(/a/g));
// [ [ 'a', 'a', 'a' ] ]

// ============================================
// PART 4: MATCH
// ============================================

console.log('hello world'.match(/world/));
// [ [ 'world', index: 6, input: 'hello world', groups: undefined ] ]

console.log('a1 b2 c3'.match(/\w\d/g));
// [ [ 'a1', 'b2', 'c3' ] ]

// ============================================
// PART 5: REPLACE
// ============================================

console.log('hello world'.replace(/world/, 'there'));
// [ 'hello there' ]

console.log('2024-01-15'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1'));
// [ '15/01/2024' ]

// ============================================
// PART 6: SPLIT
// ============================================

console.log('a,b,c'.split(','));
// [ [ 'a', 'b', 'c' ] ]

console.log('a1b2c3'.split(/\d/));
// [ [ 'a', 'b', 'c', '' ] ]

// ============================================
// PART 7: EXEC
// ============================================

const execRe = /(\d+)/;
const result = execRe.exec('abc123def');
console.log(result[0]);
// [ '123' ]
console.log(result.index);
// [ 3 ]

// ============================================
// PART 8: MATCHALL
// ============================================

const str = 'a1 b2 c3';
const all = [...str.matchAll(/(\w)(\d)/g)];
console.log(all.map(m => m[1]));
// [ [ 'a', 'b', 'c' ] ]

// ============================================
// PART 9: NAMED GROUPS
// ============================================

const named = '2024-01-15'.match(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/);
console.log(named.groups.y);
// [ '2024' ]

// ============================================
// PART 10: LOOKAHEAD
// ============================================

console.log(/\d+(?=px)/.test('100px'));
// [ true ]

console.log(/\d+(?=px)/.test('100em'));
// [ false ]

// ============================================
// PART 11: LOOKBEHIND
// ============================================

console.log('$100'.match(/(?<=\$)\d+/)[0]);
// [ '100' ]

// ============================================
// PART 12: BACKREFERENCE
// ============================================

console.log(/(\w)\1/.test('aa'));
// [ true ]

console.log(/(\w)\1/.test('ab'));
// [ false ]

// ============================================
// PART 13: GREEDY VS LAZY
// ============================================

console.log('<a><b>'.match(/<.+>/)[0]);
// [ '<a><b>' ]

console.log('<a><b>'.match(/<.+?>/)[0]);
// [ '<a>' ]

// ============================================
// PART 14: EMAIL VALIDATION
// ============================================

const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
console.log(emailRe.test('alice@example.com'));
// [ true ]

console.log(emailRe.test('not-email'));
// [ false ]

// ============================================
// PART 15: EXTRACT NUMBERS
// ============================================

console.log('abc123def456'.match(/\d+/g));
// [ [ '123', '456' ] ]

// ============================================
// PART 16: EXTRACT EMAILS
// ============================================

const text = 'Contact: alice@a.com or bob@b.org';
console.log(text.match(/[\w.+-]+@[\w.-]+\.\w+/g));
// [ [ 'alice@a.com', 'bob@b.org' ] ]

// ============================================
// PART 17: WHITESPACE CLEANUP
// ============================================

console.log('  too   much    space  '.replace(/\s+/g, ' ').trim());
// [ 'too much space' ]

// ============================================
// PART 18: FORMAT NUMBER
// ============================================

function formatNumber(n) {
  return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

console.log(formatNumber(1234567));
// [ '1,234,567' ]

// ============================================
// PART 19: ESCAPE REGEX
// ============================================

function escapeRegex(str) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

console.log(escapeRegex('a.b*c'));
// [ 'a\\.b\\*c' ]

// ============================================
// PART 20: FULL SCRIPT
// ============================================

const re49 = /hello/;
console.log(re49.test('hello world'));

const re2_49 = new RegExp('hello');
console.log(re2_49.test('hello world'));

console.log(/world/.test('hello world'));
console.log(/hello/i.test('HELLO'));
console.log('hello world'.match(/\w+/g));
console.log('2024-01-15'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1'));
console.log('a,b,c'.split(','));
console.log('abc123'.match(/\d+/)[0]);
console.log([...'abc123def456'.matchAll(/\d+/g)].map(m => m[0]));

const pattern49 = /(\w+)@(\w+)\.com/;
const result49 = pattern49.exec('contact: alice@example.com');
console.log(result49[1], result49[2]);

console.log(/^\d{4}-\d{2}-\d{2}$/.test('2024-01-15'));
console.log('phone: 555-1234'.replace(/\d{3}-\d{4}/, '***-****'));
console.log('Hello World'.replace(/\s/g, '_'));

Quick Reference

Creating Regex

FormExample
Literal/pattern/flags
Constructornew RegExp('pattern', 'flags')
Dynamicnew RegExp(var, 'g')

Flags

FlagMeaning
gGlobal
iCase-insensitive
mMultiline
sDotall
uUnicode
ySticky
dIndices

String Methods

MethodReturns
str.match(re)Match array or null
str.matchAll(re)Iterator of matches
str.replace(re, s)New string
str.replaceAll(re, s)Replace all
str.search(re)Index or -1
str.split(re)Array

Regex Methods

MethodReturns
re.test(str)Boolean
re.exec(str)Match array or null

Character Classes

PatternMatches
\dDigit
\DNon-digit
\wWord char
\WNon-word
\sWhitespace
\SNon-whitespace
.Any (excl. newline)
[abc]a, b, or c
[^abc]Not a, b, c
[a-z]Range

Quantifiers

PatternMatches
*0+
+1+
?0 or 1
{n}Exactly n
{n,}n+
{n,m}n to m
*?Lazy 0+
+?Lazy 1+

Anchors

PatternMeaning
^Start
$End
\bWord boundary
\BNon-boundary

Groups

PatternMeaning
( )Capture
(?: )Non-capture
(?<name> )Named capture
\1Backreference

Lookarounds

PatternMeaning
(?= )Lookahead
(?! )Negative lookahead
(?<= )Lookbehind
(?<! )Negative lookbehind

Common Patterns

PurposePattern
Email/^[^\s@]+@[^\s@]+\.[^\s@]+$/
URL/^https?:\/\/.+/
Date/^\d{4}-\d{2}-\d{2}$/
Time/^([01]\d|2[0-3]):[0-5]\d$/
Phone US/^\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/
Hex color/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/
ZIP/^\d{5}(-\d{4})?$/
Whitespace/\s+/g
Word/\b\w+\b/g

Best Practices

Do This:

// Use literals for static patterns
const re = /hello/i;                            // ✅

// Use constructor for dynamic patterns
const re = new RegExp(escapeRegex(input), 'g'); // ✅

// Use test for boolean checks
if (/^\d+$/.test(str)) { ... }                  // ✅

// Use match with /g for all matches
str.match(/\d+/g);                              // ✅

// Use matchAll for details
for (const m of str.matchAll(/x/g)) { ... }     // ✅

// Use named groups for clarity
/(?<year>\d{4})-(?<month>\d{2})/                // ✅

// Escape user input
new RegExp(escapeRegex(userInput));             // ✅

// Reset lastIndex with global regexes
re.lastIndex = 0;                               // ✅

// Use anchors to avoid partial matches
/^\d{4}$/.test(year);                           // ✅

Don’t Do This:

// Don't use test with g in a loop
while (re.test(s)) { ... }                      // ⚠️  infinite loop

// Don't pass user input to RegExp unescaped
new RegExp(userInput);                          // ❌ regex injection

// Don't write complex regexes without comments
/^(?=.*[a-z])(?=.*[A-Z])...$/;                  // ⚠️  unreadable

// Don't use regex for HTML parsing
/<div>.*<\/div>/                                // ❌ use a parser

// Don't forget anchors
/\d{4}/.test('abc12345');                       // ⚠️  matches substring

// Don't use regex for simple string checks
/hello/.test(str);                              // ⚠️  str.includes('hello')

// Don't confuse match vs matchAll
str.match(/x/g);                                // ✅  simple list
str.matchAll(/x/g);                             // ✅  with details

Common Pitfalls

PitfallProblemSolution
g with test in loopInfinite loopReset lastIndex or omit g
Forgot to escape .Matches any charUse \.
Greedy quantifierMatches too muchUse lazy .*?
Missing anchorsPartial matchesAdd ^ and $
Unescaped user inputRegex injectionEscape special chars
\d in character classStill worksFine — \d OK inside []
Lookbehind supportOld browsersCheck compatibility
Dot doesn’t match newlineMultiline stringsUse s flag or [\s\S]
matchAll without gThrowsAdd g flag
replace not globalOnly first matchAdd g flag or use replaceAll

Real-World Examples

1. Basic Test

console.log(/hello/.test('hello world'));
// [ true ]

2. Case Insensitive

console.log(/hello/i.test('HELLO'));
// [ true ]

3. Global Match

console.log('a a a'.match(/a/g));
// [ [ 'a', 'a', 'a' ] ]

4. Replace

console.log('hello world'.replace(/world/, 'there'));
// [ 'hello there' ]

5. Email Validation

const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
console.log(emailRe.test('alice@example.com'));
// [ true ]

6. Date Validation

const dateRe = /^\d{4}-\d{2}-\d{2}$/;
console.log(dateRe.test('2024-01-15'));
// [ true ]

console.log(dateRe.test('01-15-2024'));
// [ false ]

7. Extract Numbers

console.log('abc123def456'.match(/\d+/g));
// [ [ '123', '456' ] ]

8. Extract Emails

const text = 'Contact: alice@a.com or bob@b.org';
console.log(text.match(/[\w.+-]+@[\w.-]+\.\w+/g));
// [ [ 'alice@a.com', 'bob@b.org' ] ]

9. Capture Groups

const match = '2024-01-15'.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(match[1], match[2], match[3]);
// [ '2024' '01' '15' ]

10. Named Groups

const match = '2024-01-15'.match(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/);
console.log(match.groups.y);
// [ '2024' ]

11. Replace with Callback

const result = '2024-01-15'.replace(
  /(\d{4})-(\d{2})-(\d{2})/,
  (_, y, m, d) => `${d}.${m}.${y}`
);
console.log(result);
// [ '15.01.2024' ]

12. Whitespace Cleanup

console.log('  too   much    space  '.replace(/\s+/g, ' ').trim());
// [ 'too much space' ]

13. Format Number

function formatNumber(n) {
  return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

console.log(formatNumber(1234567));
// [ '1,234,567' ]

14. Escape Regex

function escapeRegex(str) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

console.log(escapeRegex('a.b*c'));
// [ 'a\\.b\\*c' ]

15. Dynamic Regex

function highlight(text, word) {
  const re = new RegExp(`(${escapeRegex(word)})`, 'gi');
  return text.replace(re, '<mark>$1</mark>');
}

console.log(highlight('hello world', 'world'));
// [ 'hello <mark>world</mark>' ]

16. Password Strength

const pwRe = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;
console.log(pwRe.test('MyPass123'));
// [ true ]

console.log(pwRe.test('weak'));
// [ false ]

17. Split by Multiple Delimiters

console.log('one, two;three   four'.split(/[,;\s]+/));
// [ [ 'one', 'two', 'three', 'four' ] ]

18. Match All Details

const re = /(\w)(\d)/g;
for (const m of 'a1 b2 c3'.matchAll(re)) {
  console.log(m[1], m[2], m.index);
}
// [ 'a' '1' 0 ]
// [ 'b' '2' 3 ]
// [ 'c' '3' 6 ]

19. Lookaround

console.log('$100'.match(/(?<=\$)\d+/)[0]);
// [ '100' ]

console.log(/\d+(?=px)/.test('100px'));
// [ true ]

20. Full Script

const re49 = /hello/;
console.log(re49.test('hello world'));

const re2_49 = new RegExp('hello');
console.log(re2_49.test('hello world'));

console.log(/world/.test('hello world'));
console.log(/hello/i.test('HELLO'));
console.log('hello world'.match(/\w+/g));
console.log('2024-01-15'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1'));
console.log('a,b,c'.split(','));
console.log('abc123'.match(/\d+/)[0]);
console.log([...'abc123def456'.matchAll(/\d+/g)].map(m => m[0]));

const pattern49 = /(\w+)@(\w+)\.com/;
const result49 = pattern49.exec('contact: alice@example.com');
console.log(result49[1], result49[2]);

console.log(/^\d{4}-\d{2}-\d{2}$/.test('2024-01-15'));
console.log('phone: 555-1234'.replace(/\d{3}-\d{4}/, '***-****'));
console.log('Hello World'.replace(/\s/g, '_'));

Visual: Regex Anatomy

┌──────────────────────────────────────────────┐
│  /^(\d{4})-(\d{2})-(\d{2})$/                 │
│   │ │   │     │     │                        │
│   │ │   │     │     └─── group 3             │
│   │ │   │     └───────── group 2             │
│   │ │   └─────────────── group 1             │
│   │ └─────────────────── anchor start        │
│   └───────────────────── anchor end          │
│                                              │
│  Matches: "2024-01-15"                       │
│  Groups:  ['2024', '01', '15']               │
│                                              │
└──────────────────────────────────────────────┘

Visual: test vs match vs exec

┌──────────────────────────────────────────────┐
│  re.test(str)                                │
│    → true / false                            │
│                                              │
│  str.match(re)                               │
│    → array of matches                        │
│                                              │
│  re.exec(str)                                │
│    → array with details + groups             │
│                                              │
│  str.matchAll(re)                            │
│    → iterator of all matches with details    │
│                                              │
└──────────────────────────────────────────────┘

Visual: Greedy vs Lazy

┌──────────────────────────────────────────────┐
│  Input: "<a><b><c>"                          │
│                                              │
│  Greedy: /<.+>/                              │
│    → "<a><b><c>"  (matches to last >)        │
│                                              │
│  Lazy: /<.+?>/                               │
│    → "<a>"        (matches to first >)       │
│                                              │
│  Add ? to make quantifier lazy               │
│                                              │
└──────────────────────────────────────────────┘

Summary

ConceptSyntaxExample
Literal/pattern//hello/
Constructornew RegExp('pattern')Dynamic
Flags/pattern/giGlobal, insensitive
Testre.test(str)Boolean
Execre.exec(str)Match array
Matchstr.match(re)Match array
Match allstr.matchAll(re)Iterator
Replacestr.replace(re, s)New string
Replace allstr.replaceAll(re, s)New string
Searchstr.search(re)Index
Splitstr.split(re)Array
Digit\d[0-9]
Word\w[A-Za-z0-9_]
Whitespace\s[ \t\n\r]
Any.Any except newline
Class[abc]a, b, or c
Negated[^abc]Not a, b, c
Quantifier* + ? {n,m}Repetition
Anchor^ $ \bPosition
Group( )Capture
Named group(?<name> )Named capture
Non-capture(?: )Group without capture
Lookahead(?= ) / (?! )Assert ahead
Lookbehind(?<= ) / (?<! )Assert behind
Alternation|OR

Key takeaways:

  • Regex is a pattern language — test, match, exec, replace, split are your tools
  • Use literals (/pattern/) for static, constructor (new RegExp()) for dynamic
  • Flags: g (global), i (case), m (multiline), s (dotall), u (unicode)
  • Character classes: \d, \w, \s, ., [abc], [^abc]
  • Quantifiers: *, +, ?, {n}, {n,m} — greedy by default, lazy with ?
  • Anchors: ^ and $ — always use them for validation
  • Groups: ( ) capture, (?: ) doesn’t, (?<name> ) names
  • Lookarounds assert without consuming
  • Always escape user input before putting it in a RegExp
  • Use named groups for complex patterns — makes them readable
  • test for boolean, match for arrays, exec for details
  • Watch out for lastIndex with global regexes and test
  • Regex isn’t for HTML — use a parser

Remember: Regular expressions are powerful but cryptic. Learn the building blocks — character classes, quantifiers, anchors, groups — and compose them. Use test for validation, match for extraction, replace for transformation. Escape user input. Use named groups for readability. Anchor your patterns. And don’t over-engineer — if a simple includes or split does the job, use it. Master regex, and text manipulation in JavaScript becomes trivial.


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!