Di JavaScript, regex adalah sebuah object (RegExp), dan kamu memakainya lewat beberapa method. Bayangin cetakan nastar Bu Ratna. Cetakannya satu, gerakan tangannya yang beda: cek adonan cukup buat satu kue (test()), cetak satu kue (match(), exec()), cetak semua sekaligus dan catat posisinya (matchAll()), ganti bentuknya (replace()), atau potong adonannya (split()). Pelajaran ini merangkum gerakan-gerakan itu di chat PO, plus dua flag yang mengubah cara regex berjalan: g dan y.
🧪 Tebakan cepat: chat kita punya
PO-0412.new RegExp("PO-\d{4}").test(chat)menjawabtrueataufalse? Tebak dulu, jawabannya ada di bagian "Membuat regex: literal dannew RegExp()".
Membuat regex: literal dan new RegExp()
const literal = /PO-\d{4}/g; // pola di antara dua garis miring, flag di belakang
const dariString = new RegExp("PO-\\d{4}", "g"); // pola dari string
console.log(literal.source, dariString.source); // 'PO-\d{4}' 'PO-\d{4}'
const lupa = new RegExp("PO-\d{4}");
console.log(lupa.source); // 'PO-d{4}'
console.log(lupa.test(chat)); // false
console.log(new RegExp(String.raw`PO-\d{4}`).test(chat)); // true
- Literal
/pola/flagsdipakai kalau polanya sudah pasti saat kode ditulis. Flag ditulis setelah garis miring penutup, misalnya/nastar/gi. new RegExp(teks, flags)dipakai kalau polanya baru ada saat program jalan, misalnya dari variabel atau isian pengguna.- Di string biasa, backslash dipakai JavaScript sendiri.
"\d"jadi"d", jadi polanya berubah jadiPO-d{4}: "PO-, lalu huruf d empat kali". Gak ada yang cocok di chat, jadifalse. Itu jawaban tebakannya. Tulis"\\d", atau pakaiString.rawyang membiarkan backslash apa adanya."\b"lebih parah: di string dia jadi karakter backspace. - Isian pengguna di
new RegExp()harus di-escape dulu:kata.replace(/[.*+?^${}()|[\]\\]/g, "\\amp;"). Tanpa itu,"Rp85.000"ikut cocok denganRp85x000, dan"(nastar"melemparSyntaxError: Invalid regular expression: /(nastar/: Unterminated group. Browser terbaru punyaRegExp.escape(), tapi belum ada di semua tempat (Node 22 belum punya).
test(), match(), dan matchAll()
console.log(/PO-\d{4}/.test(chat)); // true
console.log(chat.match(/PO-(\d{4})/)[1]); // '0412'
console.log(chat.match(/PO-(\d{4})/).index); // 121
console.log(chat.match(/PO-(\d{4})/g)); // ['PO-0412', 'PO-0413']
console.log("abc123def456".match(/\d+/g)); // ['123', '456']
for (const m of chat.matchAll(/PO-(\d{4})/g)) {
console.log(m[0], m[1], m.index); // 'PO-0412' '0412' 121, lalu 'PO-0413' '0413' 567
}
test()cuma menjawabtrueataufalse. Paling pas untuk validasi.match()tanpag: kecocokan pertama, lengkap dengan group danindex. Gak ada yang cocok:null.match()dengang: semua kecocokan, tapi cuma teksnya. Group danindexhilang.matchAll()memberi semua kecocokan lengkap dengan group danindex. Dia wajib pakaig; tanpagdia melemparTypeError: String.prototype.matchAll called with a non-global RegExp argument. Hasilnya iterator: pakaifor...of, atau[...chat.matchAll(pola)]untuk array.search()memberi index kecocokan pertama, atau-1:"PO-0412".search(/\d/)memberi3.
exec(), lastIndex, dan flag y
Regex dengan flag g (atau y) mengingat di mana dia berhenti, di properti lastIndex. test() dan exec() melanjutkan dari situ.
const re = /PO-(\d{4})/g;
console.log(re.test(chat), re.lastIndex); // true 128
console.log(re.test(chat), re.lastIndex); // true 574
console.log(re.test(chat), re.lastIndex); // false 0
const cari = /PO-(\d{4})/g;
let m;
while ((m = cari.exec(chat)) !== null) {
console.log(m[1], cari.lastIndex); // '0412' 128, lalu '0413' 574
}
- Panggilan ketiga gagal karena gak ada
PO-lagi sesudah posisi 574, lalulastIndexkembali ke 0. Regex yang sama, teks yang sama, hasil beda: ini bug klasik kalau regex ber-gdipakai ulang untuktest(). - Loop
exec()memanfaatkan sifat itu dengan sengaja: tiap panggilan memberi kecocokan berikutnya, sampainull.
Flag y (sticky) lebih ketat: dia cuma mencoba tepat di lastIndex, gak mencari ke depan.
const lengket = /PO-\d{4}/y;
console.log(lengket.test(chat)); // false posisi 0 isinya '['
lengket.lastIndex = 121;
console.log(lengket.test(chat)); // true
Flag y dipakai program yang membaca teks potong demi potong dari kiri, seperti parser.
replace() dan split()
console.log("Halo Dunia".replace(/dunia/i, "Regex")); // 'Halo Regex'
console.log("0812-3456-7890".replace(/-/, "")); // '08123456-7890'
console.log("0812-3456-7890".replace(/-/g, "")); // '081234567890'
console.log("tgl 25/03".replace(/(\d{2})\/(\d{2})/, "$2-$1")); // 'tgl 03-25'
const harga = chat.replace(/(\d+)(?:rb|k)\b/g, (_, angka) => "Rp" + angka + ".000");
console.log(harga.split("\n")[4]); // '[12/03/26 08.25] Sari: NASTAR 3 toples = Rp255.000 kan? ...'
console.log("+62 857-1122-3344".split(/[- ]/)); // ['+62', '857', '1122', '3344']
console.log("0812-3456-7890".split(/(-)/)); // ['0812', '-', '3456', '-', '7890']
- Tanpa
g,replace()cuma mengganti kecocokan pertama. Dengang, semuanya. Flagimembuatduniacocok denganDunia. - Teks pengganti bisa memakai isi group:
$1,$2, atault;nama>. - Pengganti juga bisa berupa function. Dia menerima seluruh kecocokan, lalu isi tiap group, dan yang dia
returnjadi penggantinya. Di sini255rbjadiRp255.000. split()dengan regex memotong di tiap pemisah yang cocok. Group di pola pemisah ikut masuk hasil.replaceAll()dengan regex juga wajibg, kalau gak:TypeError: String.prototype.replaceAll called with a non-global RegExp argument.
⚠️ Jebakan yang sering ketemu
- Backslash hilang di string.
new RegExp("\d+")sama dengan/d+/. Tulis"\\d+"atau pakaiString.raw. - Regex ber-
gdipakai ulang untuktest().lastIndex-nya terbawa, jadi hasilnya berganti-ganti. Buat validasi, jangan pakaig, atau setre.lastIndex = 0dulu. match()dengangmembuang group. Butuh group tiap kecocokan?matchAll().replace()tanpagcuma mengganti yang pertama.- Isian pengguna masuk
new RegExp()tanpa di-escape. Titik jadi "karakter apa saja", dan kurung yang gak lengkap melempar SyntaxError.
TL;DR: Regex literal ditulis /pola/flags; new RegExp("teks", "flags") untuk pola dinamis, dengan backslash ditulis dobel ("\\d") atau lewat String.raw, dan isian pengguna di-escape dulu. test() menjawab true/false. match() tanpa g memberi kecocokan pertama beserta group; dengan g memberi semua teks tanpa group, misalnya "abc123def456".match(/\d+/g) memberi ["123", "456"]. matchAll() (wajib g) memberi semuanya lengkap. Regex dengan g atau y menyimpan lastIndex, jadi test() dan exec() melanjutkan dari posisi terakhir; y cuma mencoba tepat di posisi itu. replace() mengganti yang pertama, atau semua dengan g, dan menerima $1, lt;nama>, atau function: "Halo Dunia".replace(/dunia/i, "Regex") memberi "Halo Regex". split() memotong di tiap pemisah yang cocok.