Bab 7: Regular Expressions
7.1 Pattern & Flags
Apa Itu Regex?
Regular Expression (regex) adalah "pola pencarian" untuk teks. Bisa cari, cocokkan, dan ganti teks berdasarkan pola.
Analogi: Regex itu kayak "deskripsi buronan" — bukan nama spesifik, tapi ciri-ciri. Misal: "cari semua kata yang diawali huruf besar dan diakhiri angka." Polisi (engine regex) akan scan semua teks dan tangkap yang cocok.
Membuat Regex
// Cara 1: Literal (pakai /.../)
const regex1 = /hello/;
// Cara 2: Constructor (pakai string)
const regex2 = new RegExp('hello');
// Dengan flags
const regex3 = /hello/gi; // g = global, i = case-insensitive
const regex4 = new RegExp('hello', 'gi');Flags
// i — case-Insensitive
/hello/i.test('Hello'); // true
/hello/.test('Hello'); // false
// g — Global (cari semua, bukan cuma pertama)
'ha ha ha'.match(/ha/); // ["ha"] (cuma 1)
'ha ha ha'.match(/ha/g); // ["ha", "ha", "ha"] (semua)
// m — Multiline (^ dan $ berlaku per baris)
const text = "baris1\nbaris2";
text.match(/^baris/gm); // ["baris", "baris"] (match di tiap baris)
text.match(/^baris/g); // ["baris"] (cuma awal string)
// s — dotAll (. juga match newline)
/a.b/.test('a\nb'); // false (. tidak match \n)
/a.b/s.test('a\nb'); // true
// u — Unicode (support emoji & karakter unicode)
/😀/.test('😀'); // true
/\u{1F600}/u.test('😀'); // true
// d — indices (posisi match)
const result = /hello/d.exec('say hello');
console.log(result.indices[0]); // [4, 9] (posisi start & end)Method Dasar
const text = 'Saya belajar JavaScript dan Java';
// test() — cek ada/tidak (return boolean)
/Java/.test(text); // true
// match() — cari match (return array)
text.match(/Java\w*/g); // ["JavaScript", "Java"]
// exec() — cari dengan detail (return object)
const regex = /Java(\w*)/g;
let match;
while ((match = regex.exec(text)) !== null) {
console.log(`Found: ${match[0]}, capture: ${match[1]}, index: ${match.index}`);
}
// Found: JavaScript, capture: Script, index: 14
// Found: Java, capture: , index: 29
// replace() — ganti
text.replace(/Java/g, 'Python');
// "Saya belajar PythonScript dan Python"
// search() — cari posisi pertama
text.search(/Java/); // 14
// split() — pecah string
'a, b, c'.split(/,\s*/); // ["a", "b", "c"]7.2 Character Classes
Kelas Karakter Bawaan
// \d — digit (angka 0-9)
'phone: 0812-3456'.match(/\d+/g); // ["0812", "3456"]
// \D — bukan digit
'abc123'.match(/\D+/g); // ["abc"]
// \w — word character (huruf, angka, underscore)
'hello_world 123!'.match(/\w+/g); // ["hello_world", "123"]
// \W — bukan word character
'hello world!'.match(/\W+/g); // [" ", "!"]
// \s — whitespace (spasi, tab, newline)
'hello world'.split(/\s+/); // ["hello", "world"]
// \S — bukan whitespace
' hello '.match(/\S+/g); // ["hello"]
// . — karakter apa saja (kecuali newline)
/a.c/.test('abc'); // true
/a.c/.test('a c'); // true
/a.c/.test('ac'); // false (harus ada 1 karakter)Contoh Praktis
// Validasi nomor HP Indonesia
function isValidPhone(phone) {
return /^(\+62|62|0)8\d{8,11}$/.test(phone);
}
isValidPhone('081234567890'); // true
isValidPhone('+6281234567890'); // true
isValidPhone('12345'); // false
// Cari semua angka dalam teks
'Harga: Rp 150.000, diskon 20%'.match(/\d+/g);
// ["150", "000", "20"]
// Cari kata yang diawali huruf besar
'Saya tinggal di Jakarta Selatan'.match(/[A-Z]\w*/g);
// ["Saya", "Jakarta", "Selatan"]7.3 Unicode dalam Regex
Flag u untuk Unicode
// Tanpa flag u, emoji bisa bermasalah
'😀'.length; // 2 (JS lihat sebagai 2 "karakter")
/^.$/.test('😀'); // false! (tanpa u)
/^.$/u.test('😀'); // true (dengan u)
// Unicode property escapes (\p{...})
// Butuh flag u
// Huruf dari bahasa apapun
/\p{Letter}/u.test('ñ'); // true
/\p{Letter}/u.test('你'); // true
/\p{Letter}/u.test('5'); // false
// Angka dari script apapun
/\p{Number}/u.test('٣'); // true (angka Arab)
// Script tertentu
/\p{Script=Han}/u.test('你'); // true (Chinese)
/\p{Script=Cyrillic}/u.test('Д'); // true (Russian)7.4 Anchors: ^ dan $
Posisi, Bukan Karakter
// ^ — awal string (atau awal baris dengan flag m)
/^Hello/.test('Hello World'); // true
/^Hello/.test('Say Hello'); // false
// $ — akhir string (atau akhir baris dengan flag m)
/World$/.test('Hello World'); // true
/World$/.test('World Cup'); // false
// Kombinasi: exact match
/^Hello$/.test('Hello'); // true
/^Hello$/.test('Hello World'); // false
// Multiline
const csv = `nama,umur
Yazid,25
Budi,30`;
csv.match(/^\w+/gm); // ["nama", "Yazid", "Budi"]Contoh: Validasi Format
// Email sederhana
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test('user@mail.com'); // true
// URL
/^https?:\/\/.+/.test('https://example.com'); // true
// Hanya angka
/^\d+$/.test('12345'); // true
/^\d+$/.test('123a5'); // false7.5 Multiline Mode
const text = `Baris pertama
Baris kedua
Baris ketiga`;
// Tanpa m: ^ dan $ = awal/akhir SELURUH string
text.match(/^Baris/g); // ["Baris"] (cuma pertama)
// Dengan m: ^ dan $ = awal/akhir SETIAP BARIS
text.match(/^Baris/gm); // ["Baris", "Baris", "Baris"]
// Cari baris yang diakhiri angka
const log = `error 404
success 200
error 500`;
log.match(/.*\d+$/gm); // ["error 404", "success 200", "error 500"]7.6 Word Boundary: \b
// \b — batas kata (antara \w dan \W)
/\bJava\b/.test('JavaScript'); // false (Java bukan kata utuh)
/\bJava\b/.test('I love Java'); // true
// Cari kata utuh "cat"
'concatenate cat catalog'.match(/\bcat\b/g); // ["cat"]
// Highlight kata tertentu
function highlight(text, word) {
const regex = new RegExp(`\\b${word}\\b`, 'gi');
return text.replace(regex, `<mark>${word}</mark>`);
}
highlight('Java is not JavaScript', 'Java');
// "<mark>Java</mark> is not JavaScript"7.7 Escaping
Karakter Spesial yang Perlu Di-escape
// Karakter spesial regex: . * + ? ^ $ { } [ ] ( ) | \ /
// Untuk match literal, tambahkan \ di depan
// Match titik literal
/1\.5/.test('1.5'); // true
/1\.5/.test('1x5'); // false
/1.5/.test('1x5'); // true (. = karakter apapun!)
// Match tanda tanya
/really\?/.test('really?'); // true
// Match dollar sign
/\$100/.test('$100'); // true
// Match backslash
/\\n/.test('\\n'); // true (match literal \n, bukan newline)Escape Dinamis
// Kalau pattern dari user input, escape dulu!
function escapeRegex(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const userInput = 'price: $5.00 (USD)';
const safe = escapeRegex(userInput);
// "price: \\$5\\.00 \\(USD\\)"
const regex = new RegExp(safe);
regex.test('price: $5.00 (USD)'); // true7.8 Sets & Ranges: [...]
// [abc] — salah satu dari a, b, atau c
/[aeiou]/.test('hello'); // true (ada 'e' dan 'o')
// [a-z] — range (a sampai z)
/[a-z]/.test('Hello'); // true (ada huruf kecil)
/[A-Z]/.test('hello'); // false (tidak ada huruf besar)
/[0-9]/.test('abc'); // false
// Kombinasi range
/[a-zA-Z0-9]/.test('!'); // false
/[a-zA-Z0-9_]/.test('_'); // true (sama dengan \w)
// [^...] — negasi (BUKAN karakter ini)
/[^0-9]/.test('123'); // false (semua angka)
/[^0-9]/.test('12a'); // true (ada 'a' yang bukan angka)
// Karakter spesial di dalam [...] tidak perlu escape (kecuali ] \ ^ -)
/[.+*]/.test('1+1'); // true (. di sini = literal titik!)Contoh
// Validasi hex color
/^#[0-9a-fA-F]{6}$/.test('#ff6600'); // true
/^#[0-9a-fA-F]{6}$/.test('#xyz123'); // false
// Hanya huruf dan spasi (nama orang)
/^[a-zA-Z\s]+$/.test('John Doe'); // true
/^[a-zA-Z\s]+$/.test('John123'); // false
// Karakter Indonesia (termasuk huruf beraksen)
/^[\w\s]+$/u.test('Nama Saya'); // true7.9 Quantifiers: +, *, ?,
// {n} — tepat n kali
/\d{4}/.test('2024'); // true
/\d{4}/.test('123'); // false
// {n,m} — minimal n, maksimal m kali
/\d{2,4}/.test('1'); // false
/\d{2,4}/.test('12'); // true
/\d{2,4}/.test('12345'); // true (match "1234")
// {n,} — minimal n kali (tanpa batas atas)
/\d{3,}/.test('12'); // false
/\d{3,}/.test('123456'); // true
// + — satu atau lebih (= {1,})
/\d+/.test('abc'); // false
/\d+/.test('abc1'); // true
// * — nol atau lebih (= {0,})
/\d*/.test('abc'); // true (0 digit = valid!)
/go*gle/.test('ggle'); // true
/go*gle/.test('google'); // true
/go*gle/.test('gooogle'); // true
// ? — nol atau satu (= {0,1})
/colou?r/.test('color'); // true
/colou?r/.test('colour'); // true
/colou?r/.test('colouur'); // falseContoh Praktis
// Nomor telepon: 08xx-xxxx-xxxx atau 08xxxxxxxxxx
/^08\d{2}[-\s]?\d{4}[-\s]?\d{4,5}$/.test('0812-3456-7890'); // true
/^08\d{2}[-\s]?\d{4}[-\s]?\d{4,5}$/.test('081234567890'); // true
// IP Address (simplified)
/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test('192.168.1.1'); // true
// Password: min 8 char, ada huruf besar, kecil, angka
function isStrongPassword(pwd) {
return pwd.length >= 8 &&
/[A-Z]/.test(pwd) &&
/[a-z]/.test(pwd) &&
/\d/.test(pwd);
}7.10 Greedy vs Lazy
Greedy (Default) — Ambil Sebanyak Mungkin
// Greedy: .+ ambil sebanyak mungkin karakter
const html = '<span>Hello</span> <span>World</span>';
html.match(/<span>.+<\/span>/);
// ["<span>Hello</span> <span>World</span>"]
// Greedy ambil SEMUA dari <span> pertama sampai </span> TERAKHIR!Lazy (Tambah ?) — Ambil Sesedikit Mungkin
// Lazy: .+? ambil sesedikit mungkin
html.match(/<span>.+?<\/span>/g);
// ["<span>Hello</span>", "<span>World</span>"]
// Lazy berhenti di </span> PERTAMA yang ditemui
// Semua quantifier bisa jadi lazy dengan tambah ?
// +? — satu atau lebih (lazy)
// *? — nol atau lebih (lazy)
// ?? — nol atau satu (lazy)
// {n,m}? — n sampai m (lazy)Contoh: Extract Konten Tag
// Ambil teks di antara tanda kutip
const text = 'Dia bilang "halo" dan "selamat"';
// Greedy
text.match(/"(.+)"/); // ['"halo" dan "selamat"'] — terlalu banyak!
// Lazy
text.match(/"(.+?)"/g); // ['"halo"', '"selamat"'] — pas!
// Alternatif tanpa lazy: negated character class
text.match(/"([^"]+)"/g); // ['"halo"', '"selamat"'] — lebih cepat!7.11 Capturing Groups: (...)
Menangkap Bagian dari Match
// Tanpa group — match keseluruhan
'2024-01-15'.match(/\d{4}-\d{2}-\d{2}/);
// ["2024-01-15"]
// Dengan group — tangkap bagian-bagian
'2024-01-15'.match(/(\d{4})-(\d{2})-(\d{2})/);
// ["2024-01-15", "2024", "01", "15"]
// match[0] = keseluruhan
// match[1] = group 1 (tahun)
// match[2] = group 2 (bulan)
// match[3] = group 3 (tanggal)Named Groups
// Pakai (?<name>...) untuk beri nama
const dateRegex = /(?<tahun>\d{4})-(?<bulan>\d{2})-(?<tanggal>\d{2})/;
const match = '2024-01-15'.match(dateRegex);
console.log(match.groups.tahun); // "2024"
console.log(match.groups.bulan); // "01"
console.log(match.groups.tanggal); // "15"Non-Capturing Group: (?:...)
// (?:...) — group tapi TIDAK ditangkap (untuk organisasi saja)
'Mr. Smith'.match(/(?:Mr|Mrs|Ms)\.\s(\w+)/);
// ["Mr. Smith", "Smith"]
// Group (?:Mr|Mrs|Ms) tidak masuk hasil captureReplace dengan Groups
// Ubah format tanggal: 2024-01-15 → 15/01/2024
'2024-01-15'.replace(
/(\d{4})-(\d{2})-(\d{2})/,
'$3/$2/$1'
);
// "15/01/2024"
// Dengan named groups
'2024-01-15'.replace(
/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/,
'$<d>/$<m>/$<y>'
);
// "15/01/2024"
// Dengan function
'hello world'.replace(/(\w+)\s(\w+)/, (match, p1, p2) => {
return `${p2.toUpperCase()} ${p1.toUpperCase()}`;
});
// "WORLD HELLO"7.12 Backreferences: \1, \2
Referensi ke Group Sebelumnya
// \1 = isi group pertama yang sudah di-match
// Cari kata yang diulang
'the the cat sat sat on on the mat'.match(/\b(\w+)\s\1\b/g);
// ["the the", "sat sat", "on on"]
// Cari tag HTML yang berpasangan
'<b>bold</b> <i>italic</i>'.match(/<(\w+)>.*?<\/\1>/g);
// ["<b>bold</b>", "<i>italic</i>"]
// \1 = harus sama dengan tag pembuka!
// Named backreference
/(?<quote>['"]).*?\k<quote>/.test('"hello"'); // true
/(?<quote>['"]).*?\k<quote>/.test("'hello'"); // true
/(?<quote>['"]).*?\k<quote>/.test('"hello\''); // false (tidak match!)7.13 Alternation: |
// | — atau (pilih salah satu)
/cat|dog/.test('I have a cat'); // true
/cat|dog/.test('I have a dog'); // true
/cat|dog/.test('I have a bird'); // false
// Dalam group
/I love (cats|dogs|birds)/.test('I love cats'); // true
// Contoh: validasi ekstensi file
function isImageFile(filename) {
return /\.(jpg|jpeg|png|gif|webp|svg)$/i.test(filename);
}
isImageFile('photo.JPG'); // true
isImageFile('doc.pdf'); // false
// Contoh: parse URL protocol
'https://example.com'.match(/^(https?|ftp|file):\/\//);
// ["https://", "https"]7.14 Lookahead & Lookbehind
Lookahead: (?=...) dan (?!...)
"Cek apa yang ada DI DEPAN, tapi jangan include di match."
// Positive lookahead (?=...) — harus diikuti oleh...
// Cari angka yang diikuti "px"
'12px 5em 20px'.match(/\d+(?=px)/g); // ["12", "20"]
// "px" tidak termasuk di hasil match!
// Negative lookahead (?!...) — TIDAK boleh diikuti oleh...
// Cari angka yang BUKAN diikuti "px"
'12px 5em 20px'.match(/\d+(?!px)\d*/g); // ["5"]Lookbehind: (?<=...) dan (?<!...)
"Cek apa yang ada DI BELAKANG, tapi jangan include di match."
// Positive lookbehind (?<=...) — harus didahului oleh...
// Cari angka setelah "$"
'$100 €200 $300'.match(/(?<=\$)\d+/g); // ["100", "300"]
// Negative lookbehind (?<!...) — TIDAK boleh didahului oleh...
// Cari angka yang BUKAN setelah "$"
'$100 200 $300'.match(/(?<!\$)\d+/g); // ["00", "200", "00"]
// Hmm, perlu \b untuk kata utuh:
'$100 200 $300'.match(/(?<!\$)\b\d+\b/g); // ["200"]Contoh Praktis
// Password validation (semua kondisi harus terpenuhi)
function validatePassword(pwd) {
return /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!@#$%]).{8,}$/.test(pwd);
// (?=.*[A-Z]) — harus ada huruf besar
// (?=.*[a-z]) — harus ada huruf kecil
// (?=.*\d) — harus ada angka
// (?=.*[!@#$%]) — harus ada simbol
// .{8,} — minimal 8 karakter
}
// Format angka dengan titik ribuan
function formatNumber(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.');
}
formatNumber(1500000); // "1.500.000"
// Cari harga dalam Rupiah
'Harga Rp 150.000 dan Rp 2.500.000'.match(/(?<=Rp\s?)[\d.]+/g);
// ["150.000", "2.500.000"]7.15 Catastrophic Backtracking
Apa Itu?
Regex tertentu bisa SANGAT LAMBAT (bahkan hang!) pada input tertentu karena engine mencoba terlalu banyak kombinasi.
Analogi: Bayangkan labirin dengan jutaan jalan buntu. Engine regex coba SEMUA jalan sebelum menyerah. Ini bisa bikin browser freeze!
// ❌ BAHAYA — regex ini bisa hang!
const evil = /^(a+)+$/;
evil.test('aaaaaaaaaaaaaaaaaaaaaaaa!');
// Engine coba SEMUA cara membagi 'a' ke dalam groups... SANGAT LAMBAT!
// ❌ Contoh lain yang berbahaya
/(a|aa)+$/.test('aaaaaaaaaaaaaaaaab'); // LAMBAT!
/(\d+)*$/.test('123456789012345678!'); // LAMBAT!Cara Menghindari
// ✅ Gunakan atomic groups / possessive quantifiers (via workaround)
// ✅ Hindari nested quantifiers: (a+)+, (\d+)*, (a|aa)+
// ✅ Gunakan negated character class daripada lazy dot
// ❌ Lambat
/".*?"/.test(longString);
// ✅ Cepat
/"[^"]*"/.test(longString);
// ✅ Batasi panjang input sebelum regex
function safeMatch(text, regex, maxLength = 10000) {
if (text.length > maxLength) {
throw new Error('Input terlalu panjang');
}
return text.match(regex);
}7.16 Sticky Flag: y
// Flag y — match HANYA di posisi lastIndex (tidak search forward)
const regex = /\d+/y;
const text = '123 456 789';
regex.lastIndex = 0;
console.log(regex.exec(text)); // ["123"] (match di posisi 0)
console.log(regex.lastIndex); // 3
console.log(regex.exec(text)); // null (posisi 3 = spasi, bukan digit!)
// Dengan flag g, akan skip spasi dan cari "456"
// Dengan flag y, HARUS match tepat di lastIndex
// Berguna untuk tokenizer/lexer
function tokenize(code) {
const tokens = [];
const patterns = [
{ type: 'NUMBER', regex: /\d+/y },
{ type: 'PLUS', regex: /\+/y },
{ type: 'MINUS', regex: /-/y },
{ type: 'SPACE', regex: /\s+/y },
];
let pos = 0;
while (pos < code.length) {
let matched = false;
for (const { type, regex } of patterns) {
regex.lastIndex = pos;
const match = regex.exec(code);
if (match) {
if (type !== 'SPACE') {
tokens.push({ type, value: match[0] });
}
pos = regex.lastIndex;
matched = true;
break;
}
}
if (!matched) throw new Error(`Unexpected char at ${pos}: "${code[pos]}"`);
}
return tokens;
}
tokenize('12 + 34 - 5');
// [{ type: "NUMBER", value: "12" }, { type: "PLUS", value: "+" }, ...]7.17 Methods of RegExp & String
String Methods yang Pakai Regex
const text = 'Hello World, Hello JavaScript!';
// str.match(regex) — cari match
text.match(/Hello/g); // ["Hello", "Hello"]
// str.matchAll(regex) — iterator dengan detail (HARUS flag g)
for (const match of text.matchAll(/Hello (\w+)/g)) {
console.log(`${match[0]} at index ${match.index}, capture: ${match[1]}`);
}
// "Hello World at index 0, capture: World"
// "Hello JavaScript at index 13, capture: JavaScript"
// str.search(regex) — posisi match pertama (-1 kalau tidak ada)
text.search(/World/); // 6
text.search(/Python/); // -1
// str.replace(regex, replacement)
text.replace(/Hello/g, 'Hi'); // "Hi World, Hi JavaScript!"
// Dengan function
text.replace(/(\w+)/g, (match) => match.toUpperCase());
// "HELLO WORLD, HELLO JAVASCRIPT!"
// str.replaceAll(regex, replacement) — HARUS flag g
text.replaceAll(/Hello/g, 'Bye');
// str.split(regex)
'one, two, three'.split(/,\s*/); // ["one", "two", "three"]RegExp Methods
const regex = /(\d{4})-(\d{2})-(\d{2})/g;
const text = 'Tanggal: 2024-01-15 dan 2024-06-20';
// regex.test(str) — boolean
/\d/.test('abc123'); // true
// regex.exec(str) — satu match dengan detail
let match;
while ((match = regex.exec(text)) !== null) {
console.log(`Date: ${match[0]}`);
console.log(`Year: ${match[1]}, Month: ${match[2]}, Day: ${match[3]}`);
console.log(`Position: ${match.index}`);
}Contoh: Template Engine Sederhana
function render(template, data) {
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
return data[key] !== undefined ? data[key] : match;
});
}
const result = render(
'Halo {{nama}}, umur kamu {{umur}} tahun!',
{ nama: 'Yazid', umur: 25 }
);
// "Halo Yazid, umur kamu 25 tahun!"Contoh: Slug Generator
function slugify(text) {
return text
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '') // hapus karakter spesial
.replace(/[\s_]+/g, '-') // spasi/underscore → dash
.replace(/-+/g, '-') // multiple dash → single dash
.replace(/^-|-$/g, ''); // hapus dash di awal/akhir
}
slugify('Hello World! This is a Test'); // "hello-world-this-is-a-test"
slugify(' Belajar JavaScript 2024 '); // "belajar-javascript-2024"Contoh: Validasi Lengkap
const validators = {
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
url: /^https?:\/\/(www\.)?[\w-]+(\.\w{2,})+([\/\w.-]*)*\/?$/,
phone_id: /^(\+62|62|0)8\d{8,11}$/,
hex_color: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i,
ip_v4: /^(\d{1,3}\.){3}\d{1,3}$/,
date_iso: /^\d{4}-\d{2}-\d{2}$/,
time_24h: /^([01]\d|2[0-3]):[0-5]\d$/,
username: /^[a-zA-Z]\w{2,19}$/, // 3-20 char, mulai huruf
};
function validate(type, value) {
if (!validators[type]) throw new Error(`Unknown type: ${type}`);
return validators[type].test(value);
}
validate('email', 'user@mail.com'); // true
validate('phone_id', '081234567890'); // true
validate('hex_color', '#ff6600'); // true
validate('username', 'yazid_dev'); // true⚠️ Jebakan Umum Regex
- Lupa flag
g:match()tanpaghanya return match pertama lastIndextidak reset: Regex dengangatauysimpan posisi — bisa bikin bug
const re = /a/g;
re.test('abc'); // true (lastIndex = 1)
re.test('abc'); // false! (mulai dari index 1, 'bc' tidak match /^a/)
re.lastIndex = 0; // reset manual- Backslash di string:
new RegExp('\\d+')— perlu double backslash! - Greedy default:
.+ambil sebanyak mungkin — sering tidak sesuai harapan - Catastrophic backtracking: Hindari nested quantifiers
(a+)+
🏆 Challenge
Buat "Regex Tester" sederhana:
- Input untuk pattern regex
- Input untuk flags (g, i, m, s, u)
- Textarea untuk teks yang mau di-test
- Tampilkan: apakah match (test), semua match (matchAll), dan groups
- Highlight match di teks (bungkus dengan tag
mark) - Handle error (regex invalid → tampilkan pesan error)
// Hint:
// - try/catch saat buat new RegExp (bisa throw SyntaxError)
// - matchAll untuk dapat semua match + groups
// - replace untuk highlight
// - Tampilkan match.index untuk posisiSudah paham materi ini?
Tandai sebagai selesai untuk melacak progress-mu.