Komputer cuma menyimpan angka. Character encoding adalah aturan untuk mengubah huruf jadi angka-angka itu, lalu kembali lagi jadi huruf. Kalau aturan waktu menyimpan beda dengan waktu membaca, teksnya rusak: Café tampil sebagai Café.
🧪 Tebakan cepat: satu emoji senyum di JavaScript,
"\u{1F600}".length, hasilnya 1, 2, atau 4? Tebak dulu, jawabannya ada di bagian "Kenapa panjang emoji di JavaScript itu 2".
Huruf, code point, lalu byte
Unicode memberi setiap karakter di dunia, termasuk huruf Arab, huruf Jepang, dan emoji, satu nomor unik yang disebut code point. Code point ditulis U+ lalu angka heksadesimal (basis 16: digit 0 sampai 9, lalu A sampai F). UTF-8 adalah aturan menyimpan nomor itu sebagai 1 sampai 4 byte. Satu byte adalah angka 0 sampai 255, biasa ditulis heksa 00 sampai FF:
Karakter Code point Byte UTF-8
A U+0041 41
é U+00E9 C3 A9
中 U+4E2D E4 B8 AD
emoji senyum U+1F600 F0 9F 98 80
Atermasuk ASCII, 128 karakter dasar (huruf Inggris, angka, tanda baca). Di UTF-8 tetap 1 byte, sama persis dengan ASCII.ébutuh 2 byte. Huruf Arab juga 2 byte per huruf.中(huruf Cina) dan huruf Jepang butuh 3 byte. Emoji 4 byte.
Karena itu UTF-8 jadi standar web, dipakai sekitar 98% website: cocok dengan ASCII, bisa menyimpan semua karakter Unicode, dan hemat untuk teks Inggris yang cuma 1 byte per huruf.
Mojibake: byte dibaca dengan aturan yang salah
Café dalam UTF-8 adalah 5 byte: 43 61 66 C3 A9. Kalau byte itu dibaca dengan Windows-1252, encoding lama yang menganggap 1 byte = 1 huruf, C3 jadi à dan A9 jadi ©. Hasilnya Café. Teks rusak kayak gini namanya mojibake.
Obatnya: pakai UTF-8 di semua tempat, dan kasih tahu browser. Di HTML, taruh ini di awal <head>:
<meta charset="utf-8">
Dari server, lewat header Content-Type: text/html; charset=utf-8.
Kenapa panjang emoji di JavaScript itu 2
String JavaScript disimpan dalam UTF-16, potongan 16 bit yang disebut code unit. Code point sampai U+FFFF (bagian ini disebut BMP) muat di 1 code unit. Code point di atasnya, termasuk kebanyakan emoji, butuh 2 code unit yang disebut surrogate pair. .length menghitung code unit, bukan karakter:
"Halo".length; // 4
"\u{1F600}Budi".length; // 6, emoji dihitung 2
[..."\u{1F600}Budi"].length; // 5
"\u{1F600}"adalah cara menulis emoji senyum lewat code point-nya.[...teks]memecah string per code point, jadi emoji tetap dihitung satu.
Jawaban tebakannya: 2. Untuk menghitung karakter, pakai [...teks].length.
Karakter di URL: encodeURIComponent
URL cuma boleh berisi sebagian karakter ASCII. Karakter lain, dan karakter yang punya arti di URL (spasi, &, =, ?, /, #), harus di-percent-encode: tiap byte UTF-8-nya jadi % plus dua digit heksa. encodeURIComponent mengerjakan itu:
encodeURIComponent("hello world"); // "hello%20world"
encodeURIComponent("café"); // "caf%C3%A9"
encodeURIComponent("Budi & Ani"); // "Budi%20%26%20Ani"
encodeURIComponent("c/d"); // "c%2Fd"
%C3%A9 adalah dua byte UTF-8 dari é. Untuk query string (bagian URL setelah ?), encode setiap key dan setiap value, gabung jadi key=value, lalu sambung pasangannya dengan &:
const data = { kota: "Jakarta Selatan", menu: "nasi & teh" };
Object.entries(data)
.map(([key, value]) => encodeURIComponent(key) + "=" + encodeURIComponent(value))
.join("&");
// "kota=Jakarta%20Selatan&menu=nasi%20%26%20teh"
⚠️ Jebakan yang sering ketemu
encodeURIuntuk nilai parameter.encodeURIdibuat untuk URL utuh, jadi&,=,?, dan/dibiarkan. Untuk satu nilai, pakaiencodeURIComponent.- Cuma value yang di-encode. Key juga bisa berisi spasi atau
&. Encode dua-duanya. - Emoji gabungan. Emoji keluarga dibentuk dari beberapa emoji yang direkatkan karakter tak terlihat, jadi
[...teks].length-nya lebih dari 1. Untuk menghitung yang benar-benar tampil, pakaiIntl.Segmenter. utf8di MySQL.utf8di MySQL cuma sampai 3 byte per karakter, jadi emoji gagal disimpan. Pakaiutf8mb4.
TL;DR: Unicode memberi tiap karakter nomor (code point, misalnya U+00E9), dan UTF-8 menyimpannya sebagai 1 sampai 4 byte: ASCII 1 byte, é 2, huruf Jepang 3, emoji 4. Byte yang dibaca dengan encoding lain jadi mojibake, jadi pakai UTF-8 di mana-mana dan tulis <meta charset="utf-8">. Di JavaScript, .length menghitung code unit UTF-16 (emoji = 2), sedangkan [...teks].length menghitung karakter. Untuk URL, encodeURIComponent mengubah tiap byte jadi %XX, misalnya spasi jadi %20.