Enam tipe primitive pertama sudah cukup untuk pekerjaan sehari-hari, tetapi dua kebutuhan khusus belum terjawab. Pertama: bagaimana jika presisi penuh di atas batas aman number benar-benar wajib — misalnya nomor transaksi berukuran raksasa pada sistem pembayaran? Kedua: bagaimana jika kita butuh penanda yang dijamin tidak akan tertukar dengan penanda lain, sekalipun teksnya sama persis? JavaScript menjawab keduanya dengan bigint dan Symbol.

Chapter ini memperkenalkan keduanya sebatas kebutuhan pemula: dari mana masing-masing lahir, bagaimana menulis dan memakainya, dan aturan apa yang paling sering menabrak. Keduanya jarang dipakai di awal karier, tetapi mengenali wujudnya membuat kita tidak terkejut saat bertemu di kode orang lain.

bigint: Angka Melebihi Batas Aman #

Chapter Numbers membuktikan bahwa number kehilangan presisi di atas 9007199254740991 — batasAman + 1 dan batasAman + 2 bahkan menghasilkan angka yang sama. Tipe bigint menghilangkan batas itu: angka ditulis dengan akhiran n, dan engine menyimpan seluruh digitnya dengan tepat:

js
const batasAman = Number.MAX_SAFE_INTEGER;
console.log(batasAman + 1); // 9007199254740992 — masih tepat
console.log(batasAman + 2); // 9007199254740992 — meleset, sama dengan +1

console.log(9007199254740993n + 1n); // tepat, tanpa pembulatan

Output yang Dihasilkan:

text
9007199254740992
9007199254740992
9007199254740994n

Bandingkan baris kedua dan ketiga: operasi number menghasilkan angka yang sama untuk +1 dan +2, sedangkan bigint membedakan keduanya dengan sempurna. Aritmetika bigint memakai operator yang sama — +, -, * — tetapi kedua sisi harus sama-sama bigint:

js
console.log(999999999999999999999n * 999n);

Output yang Dihasilkan:

text
998999999999999999999001n

Hasil perkalian itu melampaui batas aman number puluhan digit, dan bigint mencatat setiap digitnya utuh. Perhatikan satu detail tampilan: ketika dicetak, Node menampilkan bigint bersama akhiran n-nya, sehingga kita selalu bisa membedakannya dari number di keluaran console.

Konversi dua arah tersedia lewat BigInt() dan Number(), tetapi keduanya perlu diwaspadai:

js
console.log(BigInt(123));                // 123n
console.log(BigInt("9007199254740993")); // tetap tepat
console.log(Number(9007199254740993n));  // presisi hilang kembali

Output yang Dihasilkan:

text
123n
9007199254740993n
9007199254740992

BigInt("9007199254740993") mempertahankan seluruh digit, tetapi begitu dikonversi kembali menjadi Number, angkanya dibulatkan menjadi 9007199254740992 — masalah presisi yang tadinya teratasi langsung kembali muncul. Aturannya sederhana: angka yang wajib presisi penuh sebaiknya tetap menjadi bigint dari awal sampai akhir.

Aturan Campuran bigint dan number #

Kesalahan yang paling cepat ditemui: mencampur bigint dan number dalam satu perhitungan. Engine menolaknya dengan TypeError, bukan mengonversi salah satunya diam-diam:

js
try {
  console.log(1n + 1);
} catch (error) {
  console.log(error.name + ": " + error.message);
}

Output yang Dihasilkan:

text
TypeError: Cannot mix BigInt and other types, use explicit conversions

Pesan errornya jelas: gunakan konversi eksplisit, misalnya BigInt(1) + 1n. Penolakan keras ini sengaja dirancang — konversi otomatis bisa menghilangkan presisi tanpa kita sadari, dan presisi justru alasan utama bigint dipakai.

Symbol: Identifier yang Dijamin Unik #

Symbol menjawab kebutuhan yang berbeda: penanda yang dijamin unik. Setiap pemanggilan Symbol() menghasilkan nilai baru yang tidak akan pernah sama dengan symbol lain, sekalipun keterangannya ditulis sama persis:

js
const idA = Symbol("id");
const idB = Symbol("id");
console.log(idA === idB); // false — dua symbol berbeda
console.log(String(idA)); // Symbol(id)

Output yang Dihasilkan:

text
false
Symbol(id)

Teks "id" di dalam kurung disebut description, dan perannya hanya satu: keterangan untuk manusia saat debugging. Description tidak membuat dua symbol jadi sama — baris pertama membuktikannya dengan false. Perbandingan === pada symbol selalu menanyakan identitas: apakah keduanya hasil pemanggilan Symbol() yang sama. Itulah jaminan uniknya.

Kegunaan utamanya menyusul di section Objects: symbol dipakai sebagai kunci properti yang tidak akan bentrok dengan kunci lain, sekalipun namanya kebetulan sama. Untuk sekarang, cukup kuasai cara pembuatan dan jaminan uniknya.

Aturan Khusus Symbol dan String #

Symbol punya satu aturan yang tidak dimiliki primitive lain: symbol tidak bisa dikonversi menjadi string secara otomatis. Konversi eksplisit dengan String() sah — sudah kita lihat di atas — tetapi penyisipan otomatis melalui penggabungan atau template literal ditolak dengan error:

js
const idA = Symbol("id");
try {
  console.log("Kode: " + idA);
} catch (error) {
  console.log(error.name + ": " + error.message);
}

Output yang Dihasilkan:

text
TypeError: Cannot convert a Symbol value to a string

Aturan yang sama berlaku di dalam template literal: `Kode: ${idA}` juga memunculkan TypeError: Cannot convert a Symbol value to a string. Penolakan ini disengaja oleh standar bahasa — konversi string yang otomatis mudah menyembunyikan kesalahan pemakaian symbol di tempat yang tidak tepat. Bila teksnya memang dibutuhkan, nyatakan niatnya secara eksplisit dengan String(idA).

Contoh Lengkap: id-unik.js #

Berkas berikut merangkum kedua tipe dalam satu uji: pembuktian presisi bigint, penolakan campuran tipe, keunikan Symbol, dan penolakan konversi otomatisnya:

js id-unik.js
// id-unik.js
// Membuktikan mengapa JavaScript menyediakan tipe bigint dan symbol.

// --- Number meleset di atas batas aman ---
const batasAman = Number.MAX_SAFE_INTEGER;
console.log(batasAman);      // 9007199254740991
console.log(batasAman + 1);  // 9007199254740992 — masih tepat
console.log(batasAman + 2);  // 9007199254740992 — meleset, sama dengan +1

// --- bigint menjaga presisi berapa pun besar angkanya ---
console.log(9007199254740993n); // 9007199254740993n — tepat, tidak meleset
console.log(9007199254740993n + 1n);
console.log(999999999999999999999n * 999n);

// --- bigint dan number tidak boleh dicampur langsung ---
try {
  console.log(1n + 1);
} catch (error) {
  console.log(error.name + ": " + error.message);
}

// --- Symbol: setiap pemanggilan menghasilkan nilai yang unik ---
const idA = Symbol("id");
const idB = Symbol("id");
console.log(idA === idB); // false
console.log(String(idA)); // Symbol(id)

// --- Symbol tidak bisa dimasukkan ke string secara otomatis ---
try {
  console.log("Kode: " + idA);
} catch (error) {
  console.log(error.name + ": " + error.message);
}

Output yang Dihasilkan:

text
9007199254740991
9007199254740992
9007199254740992
9007199254740993n
9007199254740994n
998999999999999999999001n
TypeError: Cannot mix BigInt and other types, use explicit conversions
false
Symbol(id)
TypeError: Cannot convert a Symbol value to a string

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. Baris 5–8 (const batasAman = ... dst.): number tampil baik sampai batas aman, lalu +1 dan +2 menghasilkan angka yang sama — pembuktian ulang bahwa presisi hilang di atas batas.
  2. Baris 11–13 (console.log(9007199254740993n); dst.): bigint mencatat 9007199254740993 tanpa pembulatan, melanjutkan penjumlahannya dengan tepat, dan bahkan mengalikan dua angka dua puluh satu digit dengan utuh.
  3. Baris 16–20 (try { ... } catch (error) { ... }): perhitungan 1n + 1 ditolak oleh engine dengan TypeError: Cannot mix BigInt and other types, use explicit conversions; blok catch menangkapnya agar uji berikutnya tetap berjalan.
  4. Baris 23–26 (const idA = Symbol("id"); dst.): dua symbol dengan description yang sama dibandingkan — hasilnya false, bukti jaminan unik. String(idA) menghasilkan teks Symbol(id) yang cocok untuk dicetak.
  5. Baris 29–33 (try { ... } catch (error) { ... }): penggabungan string dengan symbol ditolak TypeError: Cannot convert a Symbol value to a string — konversi otomatis dilarang, konversi eksplisit tetap tersedia.

Dengan bigint dan Symbol, tujuh tipe primitive kini lengkap kita kenali. Pertanyaan berikutnya muncul secara alami: bagaimana cara memeriksa tipe sebuah nilai saat debugging? Alat resminya adalah operator typeof — topik chapter berikutnya.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

id-unik.js

Unduh berkas
javascript
// id-unik.js
// Membuktikan mengapa JavaScript menyediakan tipe bigint dan symbol.

// --- Number meleset di atas batas aman ---
const batasAman = Number.MAX_SAFE_INTEGER;
console.log(batasAman);      // 9007199254740991
console.log(batasAman + 1);  // 9007199254740992 — masih tepat
console.log(batasAman + 2);  // 9007199254740992 — meleset, sama dengan +1

// --- bigint menjaga presisi berapa pun besar angkanya ---
console.log(9007199254740993n); // 9007199254740993n — tepat, tidak meleset
console.log(9007199254740993n + 1n);
console.log(999999999999999999999n * 999n);

// --- bigint dan number tidak boleh dicampur langsung ---
try {
  console.log(1n + 1);
} catch (error) {
  console.log(error.name + ": " + error.message);
}

// --- Symbol: setiap pemanggilan menghasilkan nilai yang unik ---
const idA = Symbol("id");
const idB = Symbol("id");
console.log(idA === idB); // false
console.log(String(idA)); // Symbol(id)

// --- Symbol tidak bisa dimasukkan ke string secara otomatis ---
try {
  console.log("Kode: " + idA);
} catch (error) {
  console.log(error.name + ": " + error.message);
}