Data dari dunia nyata jarang lengkap. Siti mendaftar dengan email tetapi tidak mengisi kota, Budi mengisi semuanya, dan pelanggan lain bahkan tidak meninggalkan kontak. Ketika data tersimpan bertingkat — pelanggan, lalu kontak, lalu email — membaca cabang yang tidak ada menghentikan program dengan TypeError. Sebelum JavaScript punya jawaban khusus, programmer menulis pemeriksaan bertingkat yang panjang hanya untuk membaca satu nilai.

Jawaban itu adalah optional chaining, ditulis ?.. Operator ini membaca properti “seadanya”: jika bagian sebelum titik memang ada, akses berlanjut; jika bagian itu null atau undefined, seluruh rantai berhenti dan menghasilkan undefined tanpa error.

Object dalam Bentuk Paling Dasar #

Contoh chapter ini memakai object — wadah yang menyimpan banyak nilai berpasangan kunci-nilai, ditulis dengan kurung kurawal. Pembahasan lengkap menunggu di section Objects; untuk saat ini cukup dua bentuk berikut:

js
const pelanggan = {
  nama: "Budi Santoso",
  kontak: { email: "[email protected]" },
};

Variabel pelanggan memegang object dengan dua kunci. Nilai kunci kontak ternyata object lagi, sehingga datanya bertingkat dua lapis. Properti terbaca dengan tanda titik: pelanggan.nama menghasilkan "Budi Santoso", dan pelanggan.kontak.email menghasilkan alamat email.

Satu fakta yang perlu dipisahkan dengan tegas sebelum melangkah: membaca kunci yang tidak ada tidak otomatis menghentikan program:

js
const profil = { nama: "Budi Santoso" };
console.log(profil.alamat);

Output yang Dihasilkan:

text
undefined

Kunci alamat tidak ada, dan engine menjawab dengan tenang: undefined. Kesalahan baru terjadi ketika rantai dilanjutkan — menulis .kota setelahnya berarti membaca properti dari undefined, dan di situlah TypeError muncul. Optional chaining dibuat persis untuk sambungan kedua ini: ia mengijinkan rantai dilanjutkan tanpa risiko, sekaligus menandai di baris kode mana data boleh absen.

Rantai Akses yang Aman #

Berkas berikut mencoba semua bentuk ?. sekaligus: properti biasa, properti bertingkat, akses dengan kurung siku, pemanggilan method, dan gabungannya dengan ??:

// profil-pelanggan.js
// Membaca data pelanggan bertingkat dengan optional chaining.

const pelanggan = {
  nama: "Budi Santoso",
  kontak: {
    email: "[email protected]",
  },
};

// Rantai aman: alamat tidak ada, hasilnya undefined, bukan error.
const kota = pelanggan.alamat?.kota;
console.log("Kota:", kota);

// Kontak ada, email ikut terbaca.
const email = pelanggan.kontak?.email;
console.log("Email:", email);

// Bentuk kurung siku: kunci disimpan di variabel.
const kunci = "email";
console.log("Email via kunci:", pelanggan.kontak?.[kunci]);

// Method bawaan number juga bisa dijaga dengan ?.
const nilaiDiskon = null;
console.log("Diskon terformat:", nilaiDiskon?.toFixed(2));

// Method yang tidak ada pada object pun aman dipanggil dengan ?.
const pelangganBaru = { nama: "Siti Rahma" };
console.log(pelangganBaru.sapa?.());

// Kombinasi dengan ??: undefined digantikan nilai cadangan.
const kotaTampil = pelanggan.alamat?.kota ?? "Kota belum diisi";
console.log(kotaTampil);

Output yang Dihasilkan:

text
Kota: undefined
Email: [email protected]
Email via kunci: [email protected]
Diskon terformat: undefined
undefined
Kota belum diisi

Mari kita telaah baris-baris pentingnya:

  1. Baris 12 (const kota = pelanggan.alamat?.kota;): object pelanggan tidak punya kunci alamat, sehingga pelanggan.alamat bernilai undefined. Tanda ?. menangkapnya dan menghentikan rantai; hasil akhirnya undefined, dan program tetap hidup.
  2. Baris 16–17 (const email = pelanggan.kontak?.email;): kunci kontak ada isinya, sehingga akses berlanjut ke email. Keluaran menunjukkan alamat email Budi utuh.
  3. Baris 21 (pelanggan.kontak?.[kunci]): bentuk ?.[kunci] dipakai ketika nama properti tersimpan di variabel. Hasilnya sama dengan bentuk titik: [email protected].
  4. Baris 25 (nilaiDiskon?.toFixed(2)): ?. tidak hanya untuk object buatan sendiri. nilaiDiskon bernilai null, sehingga method toFixed milik number tidak dipanggil; keluarannya undefined. Bila nilai tadi berupa angka, toFixed(2) akan berjalan normal.
  5. Baris 29 (pelangganBaru.sapa?.()): bentuk ?.() menjaga pemanggilan method. Object pelangganBaru tidak punya method sapa, tetapi pemanggilan tetap aman dan menghasilkan undefined — baris kelima keluaran.
  6. Baris 32 (pelanggan.alamat?.kota ?? "Kota belum diisi"): rantai tadi menghasilkan undefined, lalu ?? dari chapter sebelumnya menggantinya dengan "Kota belum diisi". Kombinasi ?. dan ?? adalah pasangan yang sangat sering muncul bersama.

Tanpa ?. : Pesan yang Muncul #

Untuk merasakan bedanya, kita hapus semua tanda tanya dari baris 12 dan 29 lalu jalankan:

js
const pelangganTanpaAlamat = { nama: "Siti Rahma" };
console.log(pelangganTanpaAlamat.alamat.kota);

Output yang Dihasilkan:

text
TypeError: Cannot read properties of undefined (reading 'kota')

Transkrip error di atas berasal dari node: engine mencoba membaca kota dari undefined, dan program berhenti. Perilaku serupa terjadi pada pemanggilan method — pelangganBaru.sapa() tanpa ?.() menghasilkan TypeError: p.sapa is not a function karena sapa tidak ada.

Rantai Berhenti Sejak Tanda ?. #

Satu aturan yang mudah terlewat: begitu ?. menemukan sisi kirinya nullish, seluruh sisa rantai dilewati, bukan hanya satu sambungan. Perhatikan dua keadaan berikut pada data pesanan:

js
const data = { pesanan: null };
console.log(data.pesanan?.pengirim.id);

Output yang Dihasilkan:

text
undefined

Meskipun pengirim.id ditulis tanpa ?., keluarannya tetap undefined dan tidak ada error. Penyebabnya: data.pesanan bernilai null, sehingga ?. menghentikan evaluasi sejak titik itu — bagian .pengirim.id tidak pernah disentuh engine.

Keadaan berbeda muncul ketika pesanan justru ada, tetapi pengirim tidak:

js
const data = { pesanan: { status: "dikirim" } };
console.log(data.pesanan?.pengirim.id);

Output yang Dihasilkan:

text
TypeError: Cannot read properties of undefined (reading 'id')

Kali ini ?. lolos (karena pesanan ada), lalu pengirim yang undefined disentuh langsung oleh .id — dan program berhenti. Kesimpulannya: setiap sambungan yang berpotensi kosong butuh tanda ?. sendiri. Bentuk aman untuk data di atas adalah data.pesanan?.pengirim?.id.

Tiga Bentuk ?. dan Kegunaannya #

BentukDipakai untukHasil bila kiri nullish
obj?.propMembaca properti dengan titikundefined
obj?.[kunci]Membaca properti dengan kunci di variabelundefined
obj.metode?.()Memanggil method dengan amanundefined

Ketiganya mengikuti aturan yang sama: bagian sebelum ?. diperiksa dulu; bila null atau undefined, seluruh sisa rantai dilewati dan hasilnya undefined. Catatan penting: pemeriksaan hanya menjaga satu tautan di tempat ?. berada. Rantai panjang seperti a?.b.c tetap perlu tanda ?. di setiap sambungan yang bisa kosong — bentuk lengkapnya a?.b?.c.

Rantai aman menghasilkan undefined setiap kali ada sambungan yang kosong. Nilai undefined itu jarang ingin ditampilkan mentah-mentah ke pengguna — dan di sinilah pasangan ?? dari chapter sebelumnya berperan, seperti pada baris 32 contoh utama. Chapter berikutnya membahas cara lain memilih nilai: ternary operator, yang memilih berdasarkan kondisi, bukan berdasarkan kekosongan.

Berkas Sumber

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

profil-pelanggan.js

Unduh berkas
javascript
// profil-pelanggan.js
// Membaca data pelanggan bertingkat dengan optional chaining.

const pelanggan = {
  nama: "Budi Santoso",
  kontak: {
    email: "[email protected]",
  },
};

// Rantai aman: alamat tidak ada, hasilnya undefined, bukan error.
const kota = pelanggan.alamat?.kota;
console.log("Kota:", kota);

// Kontak ada, email ikut terbaca.
const email = pelanggan.kontak?.email;
console.log("Email:", email);

// Bentuk kurung siku: kunci disimpan di variabel.
const kunci = "email";
console.log("Email via kunci:", pelanggan.kontak?.[kunci]);

// Method bawaan number juga bisa dijaga dengan ?.
const nilaiDiskon = null;
console.log("Diskon terformat:", nilaiDiskon?.toFixed(2));

// Method yang tidak ada pada object pun aman dipanggil dengan ?.
const pelangganBaru = { nama: "Siti Rahma" };
console.log(pelangganBaru.sapa?.());

// Kombinasi dengan ??: undefined digantikan nilai cadangan.
const kotaTampil = pelanggan.alamat?.kota ?? "Kota belum diisi";
console.log(kotaTampil);