A regular expression in JavaScript is an object that describes a text pattern. You create one with a literal `/pattern/flags` or with `new RegExp('pattern', 'flags')`.
**The three most-used methods**
```javascript
const text = 'User: alice, age 30';
/\d+/.test(text) // true - does the pattern appear anywhere?
text.match(/\d+/) // ['30'] - first match as array (or null)
text.replace(/\d+/, '??') // 'User: alice, age ??' - replace first match
text.replace(/\d+/g, '??')// 'User: alice, age ??' - replace ALL (g flag)
```
**Reading a pattern character by character**
Take `/([\w.]+)@([\w.]+\.\w{2,})/` (a simple email pattern):
```
( open capturing group 1
[\w.]+ one or more: word chars or literal dot
) close group 1
@ literal "@"
( open capturing group 2
[\w.]+ one or more: word chars or literal dot
\. literal dot (escaped - plain . means "any char")
\w{2,} 2 or more word characters (the TLD)
) close group 2
```
```javascript
const m = '[email protected]'.match(/([\w.]+)@([\w.]+\.\w{2,})/);
m[0] // '[email protected]' - full match
m[1] // 'bob' - group 1
m[2] // 'example.com' - group 2
```
**Regex literal vs new RegExp**
Prefer the literal `/pattern/` — it's compiled once and doesn't need string escaping. Use `new RegExp(str)` only when the pattern is built at runtime:
```javascript
// Good: literal
const valid = /^\d{3}-\d{4}$/;
// When pattern comes from a variable:
function buildSearch(word) {
const escaped = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return new RegExp(escaped, 'gi');
}
```
**The `lastIndex` gotcha with stateful regex**
When you use the `g` or `y` flag and call `.test()` or `.exec()` on the *same* regex object multiple times, the engine remembers where it left off via `regex.lastIndex`. This causes a surprising alternating true/false pattern:
```javascript
const re = /\d+/g; // stateful because of 'g'
re.test('abc 42 def'); // true (found '42', lastIndex -> 6)
re.test('abc 42 def'); // false (resumes from index 6, finds nothing)
re.test('abc 42 def'); // true (lastIndex reset to 0 after miss)
// Safe pattern: create a fresh regex each time, or reset manually
re.lastIndex = 0;
re.test('abc 42 def'); // true (always)
```
The safest rule: use the `g` flag only with `str.match()`, `str.matchAll()`, or `str.replaceAll()`, not with repeated `.test()` calls on the same regex object.
**Capturing groups** let you extract parts of a match. The result of `.match()` without the `g` flag includes the full match at index 0, then each group.
```javascript
const log = '2024-03-15 ERROR disk full';
const m = log.match(/(\d{4}-\d{2}-\d{2}) (\w+) (.+)/);
m[1] // '2024-03-15'
m[2] // 'ERROR'
m[3] // 'disk full'
```
**Named groups** `(?<name>...)` — access via `m.groups.name`:
```javascript
const m = log.match(/(?<date>\d{4}-\d{2}-\d{2}) (?<level>\w+) (?<msg>.+)/);
m.groups.date // '2024-03-15'
m.groups.level // 'ERROR'
m.groups.msg // 'disk full'
```
Named groups are also available in `replace` via `$<name>`:
```javascript
'2024-03-15'.replace(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/, '$<d>/$<m>/$<y>')
// '15/03/2024'
```
**replace with a function**
When the replacement is a function, it receives `(fullMatch, ...groups, offset, originalStr)`:
```javascript
'hello world'.replace(/(\w+)/g, (match) => match.toUpperCase())
// 'HELLO WORLD'
'a=3, b=7'.replace(/(\w+)=(\d+)/g, (_, key, val) => `${key}=${+val * 2}`)
// 'a=6, b=14'
```
**matchAll — iterate over all group matches**
`.match()` with `g` flag returns only the full matches (groups are dropped). Use `str.matchAll(regex)` to get all matches *with* their groups:
```javascript
const text = 'alice=30, bob=25';
const re = /(\w+)=(\d+)/g;
for (const m of text.matchAll(re)) {
console.log(m[1], m[2]); // 'alice' '30' then 'bob' '25'
}
// or collect results:
const pairs = [...text.matchAll(re)].map(m => ({ name: m[1], age: +m[2] }));
// [{ name: 'alice', age: 30 }, { name: 'bob', age: 25 }]
```
`matchAll` requires the `g` flag and returns an iterator, so spread it or iterate with `for...of`.
**Non-capturing groups** `(?:...)` group without a capture index — useful for alternation:
```javascript
// Match 'colour' or 'color' — the 'u?' is just quantified, not captured
'colour and color'.match(/colou?r/g) // ['colour', 'color']
// Alternation without a capture group
'cats and dogs'.match(/(?:cat|dog)s?/g) // ['cats', 'dogs']
```
Lookahead, lookbehind, greedy/lazy, and dynamic regex
**Lookahead** asserts what comes *after* the current position without consuming it.
```javascript
// Positive lookahead (?=...) - match X only when followed by Y
'100px 200em 300px'.match(/\d+(?=px)/g) // ['100', '300'] (numbers before 'px')
// Negative lookahead (?!...) - match X only when NOT followed by Y
'100px 200em'.match(/\d+(?!px)/g) // ['200'] (numbers not before 'px')
```
**Lookbehind** (ES2018) asserts what comes *before*:
```javascript
// Positive lookbehind (?<=...) - match X only when preceded by Y
'$100 EUR200'.match(/(?<=\$)\d+/g) // ['100'] (numbers after '$')
// Negative lookbehind (?<!...) - match X only when NOT preceded by Y
'$100 EUR200'.match(/(?<!\$)\d+/g) // ['200'] (numbers not after '$')
```
**Greedy vs lazy**
Quantifiers are greedy by default. Add `?` to make them lazy:
```javascript
const html = '<b>bold</b> and <i>italic</i>';
html.match(/<.+>/g) // ['<b>bold</b> and <i>italic</i>'] greedy - one big match
html.match(/<.+?>/g) // ['<b>', '</b>', '<i>', '</i>'] lazy - each tag
```
**Escaping user input for dynamic regex**
If you build a regex from a user-supplied string, you must escape all special regex characters first — otherwise a user typing `.` or `*` breaks your pattern:
```javascript
function escapeRegex(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function highlight(text, query) {
const re = new RegExp(escapeRegex(query), 'gi');
return text.replace(re, '<mark>$&</mark>');
}
highlight('Price: 2.50', '2.50') // 'Price: <mark>2.50</mark>'
// without escaping, '.' would match any char and could over-match
```
**Common pitfalls**
- Forgetting the `g` flag: `'aaa'.replace(/a/, 'b')` gives `'baa'`, not `'bbb'`
- `.` does not match newlines by default — use `[\s\S]` or the `s` flag (`/pattern/s`)
- `^` and `$` match string boundaries, not line boundaries, unless you use the `m` flag
- Using `.test()` in a loop with the same `g` regex causes `lastIndex` drift (see Block 1)
- Regex cannot parse HTML — use the DOM instead
A ready-to-use collection of patterns for common tasks in JavaScript.
**Email (simple)**
```javascript
const EMAIL = /[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}/g;
'[email protected], [email protected]'.match(EMAIL)
// ['[email protected]', '[email protected]']
```
**ISO date**
```javascript
const ISO = /\b(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\b/g;
'Event on 2024-03-15, deadline 2024-12-31'.match(ISO)
// ['2024-03-15', '2024-12-31']
```
**URL**
```javascript
const URL_RE = /https?:\/\/[^\s<>'"]+/g;
'See https://developer.mozilla.org for more'.match(URL_RE)
// ['https://developer.mozilla.org']
```
**Slug (lowercase-with-dashes)**
```javascript
function toSlug(str) {
return str.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
}
toSlug('Hello World! 42') // 'hello-world-42'
```
**Camel to snake**
```javascript
function camelToSnake(str) {
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
}
camelToSnake('myVariableName') // 'my_variable_name'
```
**Mask credit card (show last 4)**
```javascript
'4111 1111 1111 1234'.replace(/\d(?=\d{4})/g, '*')
// '**** **** **** 1234'
```
**Quick flags reference**
```
g global - find all matches (not just first)
i case-insensitive
m multiline - ^ and $ match line boundaries
s dotAll - . matches newlines too
u unicode - enables \u{...} escapes and Unicode property classes
d hasIndices - match.indices[n] gives [start, end] per group
```
**When NOT to use regex in JavaScript**
- Parsing HTML - use `document.querySelector`, `DOMParser`, or a library
- Parsing URLs - use `new URL(str)`
- Parsing JSON - use `JSON.parse`
- Validating email strictly - regex cannot enforce RFC 5322; use a library
Regex shines for scanning and transforming plain text. For structured formats, always prefer the dedicated parser.
Extract all words from a string that start with a capital letter. Return them as an array.
function capitalWords(str) {
// your code here
}
console.log(capitalWords('Hello world, My Name is Alice')); // ['Hello', 'My', 'Name', 'Alice']
console.log(capitalWords('no capitals here')); // []
Solution
function capitalWords(str) {
return str.match(/\b[A-Z][a-zA-Z]*/g) || [];
}
Count how many times a word appears in a string (case-insensitive, whole words only).
function countWord(str, word) {
// your code here
}
console.log(countWord('The cat sat on the mat. The cat is fat.', 'the')); // 3
console.log(countWord('cats and cat and cat', 'cat')); // 3 (not 'cats')
Solution
function countWord(str, word) {
const escaped = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const re = new RegExp(`\\b${escaped}\\b`, 'gi');
return (str.match(re) || []).length;
}
Extract all URLs from a string. A URL starts with http:// or https:// and ends at whitespace or end of string.
function extractUrls(str) {
// your code here
}
const text = 'Visit https://example.com and http://foo.org/path?q=1 for details.';
console.log(extractUrls(text));
// ['https://example.com', 'http://foo.org/path?q=1']
Solution
function extractUrls(str) {
return str.match(/https?:\/\/[^\s]+/g) || [];
}
No split tab
Cookie preferences
We use necessary cookies to run the site. With your permission, we can also save your site preferences and use analytics and advertising cookies to understand usage and support the project.
Open tools in tabs.Exercises, IDE tools, and trainers stay available as site tabs.
Switch without losing context.Move between explanations, code, and utilities while keeping your place.
Use the sidebar as your map.The left panels hold navigation, settings, files, libraries, and tool controls.
PythonJavaScriptSQLite
One IDE, three practical modes
Python in the browser.Run small scripts, try libraries, and practice API requests without installing anything.
JavaScript for quick experiments.Test browser-friendly code and compare ideas next to your learning materials.
SQLite for data practice.Open the database explorer to inspect tables, write queries, and learn SQL workflows locally.
TopicIDE
Work side by side with split tabs
Keep instructions visible.Open an exercise or reference page beside the IDE instead of jumping back and forth.
Compare tools while you learn.Place regex checks, explanations, and code experiments next to each other when the task needs it.
Close the split when you are done.The workspace returns to a single focused tab, and your open site tabs remain available.
Code Typing Trainer
Or plain text
This trainer is designed for a physical keyboard.Open this section on a laptop or desktop with a wide screen. Touch typing practice will not work correctly on a phone.
Speed: 0 chars/min
0 words/min
Best speed (60s): 0 chars/min
0 words/min
Errors: 0
Total time: 0.0 s
To practice touch typing, avoid looking at your physical keyboard.