Regex di JavaScript — Regex

Membuat Regex di JavaScript Ada dua cara membuat regex di JavaScript: 1. Regex Literal const pattern = /hello/gi; Digunakan saat pattern sudah diketahui saat me

Membuat Regex di JavaScript

Ada dua cara membuat regex di JavaScript:

1. Regex Literal

const pattern = /hello/gi;

Digunakan saat pattern sudah diketahui saat menulis kode. Lebih ringkas dan umum.

2. Constructor RegExp

const pattern = new RegExp("hello", "gi");

Digunakan saat pattern dinamis (dari variabel atau input user):

function cariKata(teks, kata) {
  const regex = new RegExp(kata, "gi");
  return teks.match(regex);
}

cariKata("Budi dan budi", "budi"); // ["Budi", "budi"]

Perhatian: di constructor, backslash harus di-double:

/\d+/           // literal
new RegExp("\\d+") // constructor (escape backslash)

Method yang Menggunakan Regex

RegExp.prototype.test() — Cek Kecocokan

Mengembalikan true/false:

/^\d+$/.test("123");   // true
/^\d+$/.test("abc");   // false

RegExp.prototype.exec() — Detail Kecocokan

Mengembalikan array dengan detail atau null:

const regex = /(\d{4})-(\d{2})-(\d{2})/;
const result = regex.exec("Tanggal: 2024-03-15");

console.log(result[0]);     // "2024-03-15" (full match)
console.log(result[1]);     // "2024" (group 1)
console.log(result.index);  // 9 (posisi di string)

Dengan flag g, exec() bisa dipanggil berulang:

const regex = /\d+/g;
const teks = "Ada 3 kucing dan 12 ikan";
let match;

while ((match = regex.exec(teks)) !== null) {
  console.log(`Ditemukan "${match[0]}" di posisi ${match.index}`);
}
// Ditemukan "3" di posisi 4
// Ditemukan "12" di posisi 18

String.prototype.match() — Cari Kecocokan

// Tanpa flag g — seperti exec()
"2024-03-15".match(/(\d{4})/);
// ["2024", "2024", index: 0]

// Dengan flag g — array semua kecocokan (tanpa groups)
"Ada 3 dan 12 dan 100".match(/\d+/g);
// ["3", "12", "100"]

String.prototype.matchAll() — Semua Detail

Mengembalikan iterator dengan detail lengkap untuk setiap kecocokan:

const teks = "harga: $50, diskon: $10";
const matches = [...teks.matchAll(/\$(\d+)/g)];

matches.forEach(m => {
  console.log(`Full: ${m[0]}, Amount: ${m[1]}, Index: ${m.index}`);
});
// Full: $50, Amount: 50, Index: 7
// Full: $10, Amount: 10, Index: 21

String.prototype.search() — Cari Posisi

Seperti indexOf tapi menggunakan regex:

"Hello World 123".search(/\d/);  // 12 (posisi digit pertama)
"Hello World".search(/\d/);      // -1 (tidak ditemukan)

String.prototype.split() — Pecah String

// Pecah berdasarkan satu atau lebih spasi
"satu  dua   tiga".split(/\s+/);
// ["satu", "dua", "tiga"]

// Pecah berdasarkan berbagai separator
"nama:umur;kota,negara".split(/[;:,]/);
// ["nama", "umur", "kota", "negara"]

// Capture separator dalam hasil
"satu-dua_tiga".split(/([-_])/);
// ["satu", "-", "dua", "_", "tiga"]

Perhatian: lastIndex dan Flag g

Regex dengan flag g menyimpan state lastIndex. Ini bisa menyebabkan bug:

const regex = /\d/g;
console.log(regex.test("abc1"));  // true
console.log(regex.lastIndex);    // 4
console.log(regex.test("abc1"));  // false! (mulai dari index 4)
console.log(regex.lastIndex);    // 0
console.log(regex.test("abc1"));  // true (reset ke 0)

Solusi: jangan reuse regex g untuk test(), atau reset lastIndex manual:

regex.lastIndex = 0;

Tips Performa

Regex Testing Methodology

Regex yang "kelihatan benar" sering kali bocor di edge case. Ikuti workflow ini supaya tidak ketipu pattern kamu sendiri.

1. Iterate di regex101.com

Sebelum nempel pattern ke kode, debug dulu di regex101.com atau regexr.com. Tool ini menampilkan:

Pilih flavor yang sesuai (ECMAScript untuk JS, PCRE untuk PHP, Python untuk re) — jangan asal pilih default!

2. Mulai Sederhana, Tambah Bertahap

Jangan nulis regex 80-karakter dalam satu hentakan. Bangun bertahap:

// Iterasi 1: cocokkan 4 digit
/\d{4}/

// Iterasi 2: tambah dash + 2 digit
/\d{4}-\d{2}/

// Iterasi 3: lengkapi tanggal
/\d{4}-\d{2}-\d{2}/

// Iterasi 4: anchor dan capture
/^(\d{4})-(\d{2})-(\d{2})$/

Setiap iterasi tes dulu. Kalau pecah, kamu tahu persis langkah mana yang salah.

3. Tes Match DAN Non-Match

Pemula sering hanya tes input yang seharusnya cocok. Pattern email yang lewat "[email protected]" belum tentu menolak "@@" — kedua sisi harus diuji:

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

// Harus true (positive cases)
console.assert(emailLike.test("[email protected]"));
console.assert(emailLike.test("[email protected]"));

// Harus false (negative cases) — ini yang paling sering kelupaan!
console.assert(!emailLike.test(""));
console.assert(!emailLike.test("@gmail.com"));
console.assert(!emailLike.test("user@"));
console.assert(!emailLike.test("user gmail.com"));
console.assert(!emailLike.test("a@@b.c"));

4. Edge Case Wajib Coba

Edge case Kenapa berbahaya
String kosong "" * quantifier sering bocorkan empty match
String sangat panjang ReDoS lurking di (a+)+
Whitespace di awal/akhir \s vs trim — beda hasil
Unicode (café, emoji 😀) \w tidak cocok karakter non-ASCII secara default
Newline (\n) . tidak match newline tanpa flag s
Karakter mirip — 0 vs O, l vs 1 Bug visual yang lolos

5. Pin Flag Secara Sengaja

Jangan biarkan flag jadi "kebiasaan" — pikirkan tiap-tiap:

// JELEK: copy-paste tanpa mikir
const r = /pattern/gim;

// BAIK: tulis komentar kenapa flag itu dipakai
const r = /pattern/g;  // g: butuh semua kemunculan untuk replace

Salah flag bisa diam-diam jadi bug — g di test() bikin lastIndex stateful, i di slug-validator membuat "Foo-bar" lolos padahal seharusnya tidak.

6. Unit Test Setiap Pattern

Regex yang dipakai di production layak punya test sendiri — jangan tunggu produksi yang menemukan bug-nya:

describe("phone regex", () => {
  const r = /^(\+62|0)8\d{8,11}$/;

  test.each([
    ["08123456789", true],
    ["+6281234567890", true],
    ["", false],
    ["abc", false],
    ["08", false],
    ["08123456789012345", false], // terlalu panjang
  ])("phone(%s) → %s", (input, expected) => {
    expect(r.test(input)).toBe(expected);
  });
});

🎭 Analogi sehari-hari: Regex literal /foo/ vs RegExp constructor new RegExp("foo") = quote langsung vs cite yang dirakit. Literal = hardcode, gak bisa diubah saat runtime. Constructor = build dari string variable (dynamic). Pattern dari user input → wajib constructor + escape!

💡 g flag stateful trap: Regex dengan g punya lastIndex. Reuse di test() = bug halus.

const r = /foo/g;
r.test("foo");   // true, lastIndex=3
r.test("foo");   // false! (start dari lastIndex=3)

Solusi: jangan reuse g regex untuk test(), atau pakai String.matchAll() (no state).

⚠️ Jebakan klasik di JS:

🎯 Method cheat:

TL;DR: Regex literal /.../ untuk static, new RegExp(str) untuk dynamic. g flag stateful = bug source. matchAll() modern + safe. Pattern dari user input wajib escape (regex injection). Test methodology: iterate di regex101, test match + non-match, edge case (empty, unicode, newline).