- Read and write patterns with character classes, quantifiers and anchors
- Use the flags
g,i,m,uand the methodstest,match,matchAll,replace,split - Extract data with capturing and named groups and avoid the typical traps
Checking that a phone number looks like +994 50 123 45 67, finding every hashtag in a post, turning 2026-09-27 into 27.09.2026 — with ordinary string methods each task takes a dozen lines. A regular expression (regex) describes a text pattern in a single line. It looks cryptic at first, but it is built from a small set of bricks — once you know them, everything becomes readable.
Creating a regex and the first methods
There are two ways to create a regex: the literal /pattern/flags, or new RegExp('pattern', 'flags') when the pattern is built at run time. The main methods: regex.test(str) returns true/false; str.match(regex) gives the first match (with g, all of them); str.replace(regex, replacement) replaces; str.split(regex) splits by the pattern.
const text = 'Order 15 arrived. Order 27 is late. Call 555.';
console.log(/late/.test(text));
console.log(/LATE/i.test(text));
console.log(text.match(/\d+/)[0]);
console.log(text.match(/\d+/g));
console.log(text.replace(/Order/g, 'Parcel'));
console.log('a1b22c333'.split(/\d+/));▸ Expected output
true true 15 [ '15', '27', '555' ] Parcel 15 arrived. Parcel 27 is late. Call 555. [ 'a', 'b', 'c', '' ]
The building blocks
| Pattern | Meaning |
|---|---|
. | any character except a line break |
\d \w \s | a digit; a word character (Latin letter, digit, _); whitespace. Uppercase means the opposite: \D, \W, \S |
[abc] [a-z] [^0-9] | one character from a set; a range; a character not in the set |
* + ? | 0 or more; 1 or more; 0 or 1 |
{3} {2,5} | exactly 3 times; from 2 to 5 times |
^ $ \b | start of the string; end; a word boundary |
a|b ( ) (?<name> ) | or; a group; a named group |
const patterns = {
postcode: /^AZ\d{4}$/,
phone: /^\+994 \d{2} \d{3} \d{2} \d{2}$/,
username: /^[a-z][a-z0-9_]{2,15}$/,
};
const samples = {
postcode: ['AZ1000', 'AZ100', 'az1000'],
phone: ['+994 50 123 45 67', '+994501234567'],
username: ['leyla_07', '7leyla', 'ab'],
};
for (const [name, regex] of Object.entries(patterns)) {
const results = samples[name].map((value) => `${value}: ${regex.test(value)}`);
console.log(`${name} -> ${results.join(', ')}`);
}▸ Expected output
postcode -> AZ1000: true, AZ100: false, az1000: false phone -> +994 50 123 45 67: true, +994501234567: false username -> leyla_07: true, 7leyla: false, ab: false
+ is a special character, so we write \+ to match it literally. A username must start with a letter and be 3–16 characters long in total.Flags
g— global: all matches (required formatchAlland forreplaceAllwith a regex);i— ignore case;m— multiline:^and$mean the start and end of every line;s—.also matches a line break;u— Unicode: emoji and\p{L}(a letter of any alphabet) work correctly.
const words = 'Gəncə şəhəri';
console.log(words.match(/\w+/g));
console.log(words.match(/\p{L}+/gu));
const poem = 'first line\nsecond line\nthird line';
console.log(poem.match(/^\w+/gm));▸ Expected output
[ 'G', 'nc', 'h', 'ri' ] [ 'Gəncə', 'şəhəri' ] [ 'first', 'second', 'third' ]
\w knows only the letters of the English alphabet, so letters outside it break the words apart. For Azerbaijani, Russian or Turkish text, use \p{L} with the u flag.Groups and replacing
Parentheses capture parts of the match. match without g returns an array: [full match, group1, group2, ...], and named groups are in .groups. matchAll (which requires g) gives every match together with its groups. In replace, $1 and $<name> refer to groups, and a replacement function receives the match and the groups as arguments.
const date = '2026-09-27';
const match = date.match(/^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/);
console.log(match[1], match.groups.month, match.groups.day);
console.log(date.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3.$2.$1'));
const post = 'Learning #javascript and #regex at #Educora';
const tags = [...post.matchAll(/#(\w+)/g)].map((m) => m[1].toLowerCase());
console.log(tags);
const prices = 'tea 2 AZN, bread 1 AZN';
console.log(prices.replace(/(\d+) AZN/g, (whole, amount) => `${amount * 2} AZN`));▸ Expected output
2026 09 27 27.09.2026 [ 'javascript', 'regex', 'educora' ] tea 4 AZN, bread 2 AZN
const html = '<b>bold</b> and <i>italic</i>';
console.log(html.match(/<.*>/)[0]);
console.log(html.match(/<.*?>/g));
const hasDigit = /\d/g;
console.log(hasDigit.test('a1'), hasDigit.test('a1'), hasDigit.test('a1'));
console.log('1.5'.match(/\d.\d/)[0], '1x5'.match(/\d.\d/)[0], /\d\.\d/.test('1x5'));▸ Expected output
<b>bold</b> and <i>italic</i> [ '<b>', '</b>', '<i>', '</i>' ] true false true 1.5 1x5 false
Write isStrongPassword(password): the password must be at least 8 characters long and contain at least one digit and one uppercase Latin letter. Use a separate regex and test for each rule.
function isStrongPassword(password) {
// 8+ characters, a digit, an uppercase letter
}
for (const password of ['secret', 'Secret123', 'secretpass1', 'SECRETPASS']) {
console.log(`${password}: ${isStrongPassword(password)}`);
}▸ Expected output
secret: false Secret123: true secretpass1: false SECRETPASS: false
Convert every DD.MM.YYYY date in the text to ISO format YYYY-MM-DD and print the result. Then print how many dates the text contains as Dates: N.
const text = 'Exams: 05.06.2026 and 12.06.2026. Holidays from 20.06.2026.';
// 1) replace every DD.MM.YYYY with YYYY-MM-DD and print the text
// 2) print Dates: N▸ Expected output
Exams: 2026-06-05 and 2026-06-12. Holidays from 2026-06-20. Dates: 3
Key points
- A regex is a text pattern:
/pattern/flagsornew RegExp(...). - Building blocks: classes (
\d,\w,[a-z]), quantifiers (+,*,?,{n,m}), anchors (^,$,\b), groups and|. - Flags:
gall matches,iignore case,mmultiline,uUnicode (\p{L}). testchecks,match/matchAllextract, andreplacetransforms with$1,$<name>or a function.- Anchor validation patterns; beware of greedy
.*,lastIndexwithgandtest, and unescaped special characters.
Check yourself
10 questions. Every correct answer earns XP.
/^\d{3}$/ match?