Bab 7: Regular Expressions

3 menit baca

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

javascript
// 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

javascript
// 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

javascript
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

javascript
// \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

javascript
// 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

javascript
// 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

javascript
// ^ — 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

javascript
// 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');  // false

7.5 Multiline Mode

javascript
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

javascript
// \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

javascript
// 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

javascript
// 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)');  // true

7.8 Sets & Ranges: [...]

javascript
// [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

javascript
// 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');  // true

7.9 Quantifiers: +, *, ?,

javascript
// {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'); // false

Contoh Praktis

javascript
// 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

javascript
// 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

javascript
// 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

javascript
// 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

javascript
// 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

javascript
// 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: (?:...)

javascript
// (?:...) — 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 capture

Replace dengan Groups

javascript
// 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

javascript
// \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: |

javascript
// | — 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."

javascript
// 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."

javascript
// 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

javascript
// 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!

javascript
// ❌ 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

javascript
// ✅ 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

javascript
// 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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

  1. Lupa flag g: match() tanpa g hanya return match pertama
  2. lastIndex tidak reset: Regex dengan g atau y simpan posisi — bisa bikin bug
javascript
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
  1. Backslash di string: new RegExp('\\d+') — perlu double backslash!
  2. Greedy default: .+ ambil sebanyak mungkin — sering tidak sesuai harapan
  3. Catastrophic backtracking: Hindari nested quantifiers (a+)+

🏆 Challenge

Buat "Regex Tester" sederhana:

  1. Input untuk pattern regex
  2. Input untuk flags (g, i, m, s, u)
  3. Textarea untuk teks yang mau di-test
  4. Tampilkan: apakah match (test), semua match (matchAll), dan groups
  5. Highlight match di teks (bungkus dengan tag mark)
  6. Handle error (regex invalid → tampilkan pesan error)
javascript
// Hint:
// - try/catch saat buat new RegExp (bisa throw SyntaxError)
// - matchAll untuk dapat semua match + groups
// - replace untuk highlight
// - Tampilkan match.index untuk posisi

Sudah paham materi ini?

Tandai sebagai selesai untuk melacak progress-mu.