Buka kode JavaScript yang lebih aman, bersih, dan tangguh dengan Optional Chaining (?.) dan Nullish Coalescing (??). Cegah kesalahan runtime umum dan tangani data yang hilang dengan baik.
JavaScript Optional Chaining dan Nullish Coalescing: Membangun Aplikasi yang Kuat & Tangguh
Di dunia pengembangan web yang dinamis, aplikasi JavaScript sering berinteraksi dengan berbagai sumber data, mulai dari REST API hingga input pengguna dan pustaka pihak ketiga. Aliran informasi yang konstan ini berarti bahwa struktur data tidak selalu dapat diprediksi atau lengkap. Salah satu masalah paling umum yang dihadapi pengembang adalah mencoba mengakses properti dari objek yang mungkin null atau undefined, yang mengarah pada error yang ditakuti "TypeError: Cannot read properties of undefined (reading 'x')". Error ini dapat merusak aplikasi Anda, mengganggu pengalaman pengguna, dan membuat kode Anda terlihat berantakan dengan pemeriksaan defensif.
Untungnya, JavaScript modern telah memperkenalkan dua operator yang kuat – Optional Chaining (?.) dan Nullish Coalescing (??) – yang dirancang khusus untuk mengatasi tantangan ini. Fitur-fitur ini, yang distandarisasi dalam ES2020, memungkinkan pengembang di seluruh dunia untuk menulis kode yang lebih bersih, lebih tangguh, dan kuat saat berhadapan dengan data yang berpotensi hilang. Panduan komprehensif ini akan membahas secara mendalam setiap operator ini, mengeksplorasi fungsionalitas, manfaat, kasus penggunaan lanjutan, dan bagaimana mereka bekerja secara sinergis untuk menciptakan aplikasi yang lebih dapat diprediksi dan bebas dari error.
Baik Anda seorang pengembang JavaScript berpengalaman yang membangun solusi perusahaan yang kompleks atau baru memulai perjalanan Anda, menguasai optional chaining dan nullish coalescing akan secara signifikan meningkatkan kecakapan coding Anda dan membantu Anda membangun aplikasi yang menangani ketidakpastian data dunia nyata dengan baik.
Masalahnya: Menavigasi Data yang Berpotensi Hilang
Sebelum munculnya optional chaining dan nullish coalescing, pengembang harus mengandalkan pemeriksaan kondisional yang bertele-tele dan berulang untuk mengakses properti bersarang dengan aman. Mari kita pertimbangkan skenario umum: mengakses detail alamat pengguna, yang mungkin tidak selalu ada di objek pengguna yang diterima dari API.
Pendekatan Tradisional dan Keterbatasannya
1. Menggunakan Operator Logika AND (&&)
Ini adalah teknik populer untuk melakukan short-circuiting pada akses properti. Jika ada bagian dari rantai yang falsy, ekspresi akan berhenti dan mengembalikan nilai falsy tersebut.
const user = {
id: 'u123',
name: 'Alice Smith',
contact: {
email: 'alice@example.com',
phone: '123-456-7890'
}
// alamat tidak ada
};
// Mencoba mendapatkan jalan dari user.address
const street = user && user.contact && user.contact.address && user.contact.address.street;
console.log(street); // undefined
const userWithAddress = {
id: 'u124',
name: 'Bob Johnson',
contact: {
email: 'bob@example.com'
},
address: {
street: '123 Main St',
city: 'Metropolis',
country: 'USA'
}
};
const city = userWithAddress && userWithAddress.address && userWithAddress.address.city;
console.log(city); // 'Metropolis'
// Bagaimana jika `user` itu sendiri null atau undefined?
const nullUser = null;
const streetFromNullUser = nullUser && nullUser.address && nullUser.address.street;
console.log(streetFromNullUser); // null (aman, tapi bertele-tele)
Meskipun pendekatan ini mencegah error, ia memiliki kekurangan:
- Bertele-tele: Setiap tingkat nesting memerlukan pemeriksaan berulang.
- Redundan: Nama variabel diulang beberapa kali.
- Berpotensi Menyesatkan: Ia dapat mengembalikan nilai falsy apa pun (seperti
0,'',false) jika ditemui dalam rantai, yang mungkin bukan perilaku yang diinginkan ketika secara spesifik memeriksanullatauundefined.
2. Pernyataan If Bersarang
Pola umum lainnya melibatkan pemeriksaan eksplisit keberadaan di setiap tingkat.
let country = 'Unknown';
if (userWithAddress) {
if (userWithAddress.address) {
if (userWithAddress.address.country) {
country = userWithAddress.address.country;
}
}
}
console.log(country); // 'USA'
// Dengan objek pengguna yang tidak memiliki alamat:
const anotherUser = {
id: 'u125',
name: 'Charlie Brown'
};
let postcode = 'N/A';
if (anotherUser && anotherUser.address && anotherUser.address.postcode) {
postcode = anotherUser.address.postcode;
}
console.log(postcode); // 'N/A'
Pendekatan ini, meskipun eksplisit, mengarah ke kode yang berindentasi dalam dan sulit dibaca, yang biasa dikenal sebagai "callback hell" atau "pyramid of doom" ketika diterapkan pada akses properti. Ini tidak dapat diskalakan dengan baik untuk struktur data yang lebih kompleks.
Metode tradisional ini menyoroti kebutuhan akan solusi yang lebih elegan dan ringkas untuk menavigasi data yang berpotensi hilang dengan aman. Di sinilah optional chaining masuk sebagai pengubah permainan untuk pengembangan JavaScript modern.
Memperkenalkan Optional Chaining (?.): Navigator Aman Anda
Optional Chaining adalah tambahan fantastis untuk JavaScript yang memungkinkan Anda membaca nilai properti yang terletak jauh di dalam rantai objek yang terhubung tanpa harus secara eksplisit memvalidasi bahwa setiap referensi dalam rantai tersebut valid. Operator ?. bekerja mirip dengan operator chaining ., tetapi alih-alih melempar error jika referensi adalah null atau undefined, ia melakukan "short-circuit" dan mengembalikan undefined.
Cara Kerja Optional Chaining
Ketika Anda menggunakan operator optional chaining (?.) dalam ekspresi seperti obj?.prop, mesin JavaScript pertama-tama mengevaluasi obj. Jika obj bukan null atau undefined, maka ia melanjutkan untuk mengakses prop. Jika obj *adalah* null atau undefined, seluruh ekspresi segera dievaluasi menjadi undefined, dan tidak ada error yang dilempar.
Perilaku ini meluas melalui beberapa tingkat nesting dan berfungsi untuk properti, metode, dan elemen array.
Sintaks dan Contoh Praktis
1. Akses Properti Opsional
Ini adalah kasus penggunaan yang paling umum, memungkinkan Anda untuk mengakses properti objek bersarang dengan aman.
const userProfile = {
id: 'p001',
name: 'Maria Rodriguez',
location: {
city: 'Barcelona',
country: 'Spain'
},
preferences: null // objek preferences adalah null
};
const companyData = {
name: 'Global Corp',
address: {
street: '456 Tech Ave',
city: 'Singapore',
postalCode: '123456'
},
contactInfo: undefined // contactInfo adalah undefined
};
// Mengakses properti bersarang dengan aman
console.log(userProfile?.location?.city); // 'Barcelona'
console.log(userProfile?.preferences?.theme); // undefined (karena preferences adalah null)
console.log(companyData?.contactInfo?.email); // undefined (karena contactInfo adalah undefined)
console.log(userProfile?.nonExistentProperty?.anotherOne); // undefined
// Tanpa optional chaining, ini akan melempar error:
// console.log(userProfile.preferences.theme); // TypeError: Cannot read properties of null (reading 'theme')
// console.log(companyData.contactInfo.email); // TypeError: Cannot read properties of undefined (reading 'email')
2. Panggilan Metode Opsional
Anda juga dapat menggunakan optional chaining saat memanggil metode yang mungkin tidak ada pada objek. Jika metode tersebut null atau undefined, ekspresi akan dievaluasi menjadi undefined, dan metode tidak dipanggil.
const analyticsService = {
trackEvent: (name, data) => console.log(`Melacak event: ${name} dengan data:`, data)
};
const userService = {}; // Tidak ada metode 'log' di sini
analyticsService.trackEvent?.('user_login', { userId: 'u123' });
// Output yang diharapkan: Melacak event: user_login dengan data: { userId: 'u123' }
userService.log?.('Pengguna diperbarui', { id: 'u124' });
// Output yang diharapkan: Tidak terjadi apa-apa, tidak ada error yang dilempar. Ekspresi mengembalikan undefined.
Ini sangat berguna ketika berhadapan dengan callback opsional, plugin, atau feature flag di mana sebuah fungsi mungkin ada secara kondisional.
3. Akses Array/Notasi Kurung Siku Opsional
Optional chaining juga bekerja dengan notasi kurung siku untuk mengakses elemen dalam array atau properti dengan karakter khusus.
const userActivities = {
events: ['login', 'logout', 'view_profile'],
purchases: []
};
const globalSettings = {
'app-name': 'My App',
'version-info': {
'latest-build': '1.0.0'
}
};
console.log(userActivities?.events?.[0]); // 'login'
console.log(userActivities?.purchases?.[0]); // undefined (array kosong, jadi elemen di indeks 0 adalah undefined)
console.log(userActivities?.preferences?.[0]); // undefined (preferences tidak terdefinisi)
// Mengakses properti dengan tanda hubung menggunakan notasi kurung siku
console.log(globalSettings?.['app-name']); // 'My App'
console.log(globalSettings?.['version-info']?.['latest-build']); // '1.0.0'
console.log(globalSettings?.['config']?.['env']); // undefined
Manfaat Utama dari Optional Chaining
-
Keterbacaan dan Keringkasan: Ini secara dramatis mengurangi jumlah kode boilerplate yang dibutuhkan untuk pemeriksaan defensif. Kode Anda menjadi jauh lebih bersih dan lebih mudah dipahami sekilas.
// Sebelum const regionCode = (user && user.address && user.address.country && user.address.country.region) ? user.address.country.region : 'N/A'; // Sesudah const regionCode = user?.address?.country?.region ?? 'N/A'; // (menggabungkan dengan nullish coalescing untuk nilai default) -
Pencegahan Error: Menghilangkan crash
TypeErrorsaat runtime yang disebabkan oleh upaya mengakses properti darinullatauundefined. Ini mengarah pada aplikasi yang lebih stabil. - Pengalaman Pengembang yang Lebih Baik: Pengembang dapat lebih fokus pada logika bisnis daripada pemrograman defensif, yang mengarah pada siklus pengembangan yang lebih cepat dan lebih sedikit bug.
- Penanganan Data yang Anggun: Ini memungkinkan aplikasi untuk menangani dengan baik skenario di mana data mungkin tersedia sebagian atau terstruktur secara berbeda dari yang diharapkan, yang umum terjadi saat berhadapan dengan API eksternal atau konten yang dibuat pengguna dari berbagai sumber internasional. Misalnya, detail kontak pengguna mungkin opsional di beberapa wilayah tetapi wajib di wilayah lain.
Kapan Menggunakan dan Kapan Tidak Menggunakan Optional Chaining
Meskipun optional chaining sangat berguna, penting untuk memahami penerapannya yang tepat:
Gunakan Optional Chaining Ketika:
-
Sebuah properti atau metode benar-benar opsional: Ini berarti dapat diterima jika referensi perantara adalah
nullatauundefined, dan aplikasi Anda dapat melanjutkan tanpanya, mungkin dengan menggunakan nilai default.const dashboardConfig = { theme: 'dark', modules: [ { name: 'Analytics', enabled: true }, { name: 'Reports', enabled: false } ] }; // Jika modul 'notifications' bersifat opsional const notificationsEnabled = dashboardConfig.modules.find(m => m.name === 'Notifications')?.enabled; console.log(notificationsEnabled); // undefined jika tidak ditemukan - Berurusan dengan respons API yang mungkin memiliki struktur yang tidak konsisten: Data dari endpoint yang berbeda atau versi API yang berbeda terkadang dapat menghilangkan beberapa field. Optional chaining membantu Anda mengonsumsi data tersebut dengan aman.
-
Mengakses properti pada objek yang dibuat secara dinamis atau disediakan oleh pengguna: Ketika Anda tidak dapat menjamin bentuk suatu objek,
?.menyediakan jaring pengaman.
Hindari Optional Chaining Ketika:
-
Sebuah properti atau metode sangat penting dan *harus* ada: Jika ketiadaan properti menunjukkan bug parah atau keadaan yang tidak valid, Anda harus membiarkan
TypeErrordilempar sehingga Anda dapat mendeteksi dan memperbaiki masalah yang mendasarinya. Menggunakan?.di sini akan menutupi masalah tersebut.// Jika 'userId' mutlak penting untuk setiap objek pengguna const user = { name: 'Jane' }; // 'id' tidak ada // TypeError di sini akan menunjukkan masalah integritas data yang serius // console.log(user?.id); // Mengembalikan undefined, berpotensi menutupi error // Lebih baik biarkan error atau periksa secara eksplisit: if (!user.id) { throw new Error('User ID tidak ada dan wajib diisi!'); } -
Kejelasan berkurang karena chaining yang berlebihan: Meskipun ringkas, rantai opsional yang sangat panjang (misalnya,
obj?.prop1?.prop2?.prop3?.prop4?.prop5) bisa menjadi sulit dibaca. Terkadang, memecahnya atau merestrukturisasi data Anda mungkin lebih baik. -
Anda perlu membedakan antara
null/undefineddan nilai falsy lainnya (0,'',false): Optional chaining hanya memeriksanullatauundefined. Jika Anda perlu menangani nilai falsy lainnya secara berbeda, Anda mungkin memerlukan pemeriksaan yang lebih eksplisit atau menggabungkannya dengan Nullish Coalescing, yang akan kita bahas selanjutnya.
Memahami Nullish Coalescing (??): Nilai Default yang Tepat
Sementara optional chaining membantu Anda mengakses properti dengan aman yang *mungkin* tidak ada, Nullish Coalescing (??) membantu Anda memberikan nilai default secara spesifik ketika sebuah nilai adalah null atau undefined. Ini sering digunakan bersamaan dengan optional chaining, tetapi memiliki perilaku yang berbeda dan memecahkan masalah yang berbeda dari operator logika OR (||) tradisional.
Cara Kerja Nullish Coalescing
Operator nullish coalescing (??) mengembalikan operan kanannya ketika operan kirinya adalah null atau undefined, dan sebaliknya mengembalikan operan kirinya. Ini adalah perbedaan penting dari || karena tidak memperlakukan nilai falsy lainnya (seperti 0, '', false) sebagai nullish.
Perbedaan dari Logika OR (||)
Ini mungkin konsep yang paling penting untuk dipahami saat memahami ??.
-
Logika OR (
||): Mengembalikan operan kanan jika operan kiri adalah nilai falsy apa pun (false,0,'',null,undefined,NaN). -
Nullish Coalescing (
??): Mengembalikan operan kanan hanya jika operan kiri adalah secara spesifiknullatauundefined.
Mari kita lihat contoh untuk memperjelas perbedaan ini:
// Contoh 1: Dengan 'null' atau 'undefined'
const nullValue = null;
const undefinedValue = undefined;
const defaultValue = 'Nilai Default';
console.log(nullValue || defaultValue); // 'Nilai Default'
console.log(nullValue ?? defaultValue); // 'Nilai Default'
console.log(undefinedValue || defaultValue); // 'Nilai Default'
console.log(undefinedValue ?? defaultValue); // 'Nilai Default'
// --- Perilaku berbeda di sini ---
// Contoh 2: Dengan 'false'
const falseValue = false;
console.log(falseValue || defaultValue); // 'Nilai Default' (|| menganggap false sebagai falsy)
console.log(falseValue ?? defaultValue); // false (?? menganggap false sebagai nilai yang valid)
// Contoh 3: Dengan '0'
const zeroValue = 0;
console.log(zeroValue || defaultValue); // 'Nilai Default' (|| menganggap 0 sebagai falsy)
console.log(zeroValue ?? defaultValue); // 0 (?? menganggap 0 sebagai nilai yang valid)
// Contoh 4: Dengan string kosong ''
const emptyString = '';
console.log(emptyString || defaultValue); // 'Nilai Default' (|| menganggap '' sebagai falsy)
console.log(emptyString ?? defaultValue); // '' (?? menganggap '' sebagai nilai yang valid)
// Contoh 5: Dengan NaN
const nanValue = NaN;
console.log(nanValue || defaultValue); // 'Nilai Default' (|| menganggap NaN sebagai falsy)
console.log(nanValue ?? defaultValue); // NaN (?? menganggap NaN sebagai nilai yang valid)
Poin utamanya adalah bahwa ?? memberikan kontrol yang jauh lebih tepat atas nilai default. Jika 0, false, atau string kosong '' dianggap sebagai nilai yang valid dan bermakna dalam logika aplikasi Anda, maka ?? adalah operator yang harus Anda gunakan untuk mengatur default, karena || akan salah menggantinya.
Sintaks dan Contoh Praktis
1. Mengatur Nilai Konfigurasi Default
Ini adalah kasus penggunaan yang sempurna untuk nullish coalescing, memastikan bahwa pengaturan eksplisit yang valid (bahkan jika falsy) dipertahankan, sementara pengaturan yang benar-benar hilang mendapatkan default.
const userSettings = {
theme: 'light',
fontSize: 14,
enableNotifications: false, // Pengguna secara eksplisit mengatur ke false
animationSpeed: null // animationSpeed secara eksplisit diatur ke null (mungkin untuk mewarisi default)
};
const defaultSettings = {
theme: 'dark',
fontSize: 16,
enableNotifications: true,
animationSpeed: 300
};
const currentTheme = userSettings.theme ?? defaultSettings.theme;
console.log(`Tema Saat Ini: ${currentTheme}`); // 'light'
const currentFontSize = userSettings.fontSize ?? defaultSettings.fontSize;
console.log(`Ukuran Font Saat Ini: ${currentFontSize}`); // 14 (bukan 16, karena 0 adalah angka yang valid)
const notificationsEnabled = userSettings.enableNotifications ?? defaultSettings.enableNotifications;
console.log(`Notifikasi Diaktifkan: ${notificationsEnabled}`); // false (bukan true, karena false adalah boolean yang valid)
const animationDuration = userSettings.animationSpeed ?? defaultSettings.animationSpeed;
console.log(`Durasi Animasi: ${animationDuration}`); // 300 (karena animationSpeed adalah null)
const language = userSettings.language ?? 'en-US'; // language tidak terdefinisi
console.log(`Bahasa yang Dipilih: ${language}`); // 'en-US'
2. Menangani Parameter API Opsional atau Input Pengguna
Saat membuat permintaan API atau memproses pengiriman formulir pengguna, beberapa field mungkin bersifat opsional. ?? membantu Anda menetapkan default yang masuk akal tanpa menimpa nilai nol atau false yang sah.
function searchProducts(query, options) {
const resultsPerPage = options?.limit ?? 20; // Default ke 20 jika limit null/undefined
const minPrice = options?.minPrice ?? 0; // Default ke 0, memungkinkan 0 sebagai harga min yang valid
const sortBy = options?.sortBy ?? 'relevance';
console.log(`Mencari: '${query}'`);
console.log(` Hasil per halaman: ${resultsPerPage}`);
console.log(` Harga minimum: ${minPrice}`);
console.log(` Urutkan berdasarkan: ${sortBy}`);
}
searchProducts('laptops', { limit: 10, minPrice: 500 });
// Diharapkan:
// Mencari: 'laptops'
// Hasil per halaman: 10
// Harga minimum: 500
// Urutkan berdasarkan: relevance
searchProducts('keyboards', { minPrice: 0, sortBy: null }); // minPrice adalah 0, sortBy adalah null
// Diharapkan:
// Mencari: 'keyboards'
// Hasil per halaman: 20
// Harga minimum: 0
// Urutkan berdasarkan: relevance (karena sortBy adalah null)
searchProducts('monitors', {}); // Tidak ada opsi yang diberikan
// Diharapkan:
// Mencari: 'monitors'
// Hasil per halaman: 20
// Harga minimum: 0
// Urutkan berdasarkan: relevance
Manfaat Utama dari Nullish Coalescing
-
Presisi dalam Nilai Default: Memastikan bahwa hanya nilai yang benar-benar hilang (
nullatauundefined) yang diganti dengan default, menjaga nilai falsy yang valid seperti0,'', ataufalse. -
Niat yang Lebih Jelas: Secara eksplisit menyatakan bahwa Anda hanya ingin memberikan fallback untuk
nullatauundefined, membuat logika kode Anda lebih transparan. -
Ketangguhan: Mencegah efek samping yang tidak diinginkan di mana
0ataufalseyang sah mungkin telah digantikan oleh default saat menggunakan||. -
Aplikasi Global: Presisi ini sangat penting untuk aplikasi yang berurusan dengan berbagai jenis data, seperti aplikasi keuangan di mana
0adalah nilai yang signifikan, atau pengaturan internasionalisasi di mana string kosong mungkin mewakili pilihan yang disengaja.
Pasangan Kuat: Optional Chaining dan Nullish Coalescing Bersama
Meskipun kuat secara individual, optional chaining dan nullish coalescing benar-benar bersinar ketika digunakan dalam kombinasi. Sinergi ini memungkinkan akses data yang sangat kuat dan ringkas dengan penanganan default yang tepat. Anda dapat dengan aman menelusuri struktur objek yang berpotensi hilang dan kemudian, jika nilai akhirnya adalah null atau undefined, segera memberikan fallback yang bermakna.
Contoh Sinergis
1. Mengakses Properti Bersarang dengan Fallback Default
Ini adalah kasus penggunaan gabungan yang paling umum dan berdampak.
const userData = {
id: 'user-007',
name: 'James Bond',
contactDetails: {
email: 'james.bond@mi6.gov.uk',
phone: '007-007-0070'
},
// preferences tidak ada
address: {
street: 'Whitehall St',
city: 'London'
// postcode tidak ada
}
};
const clientData = {
id: 'client-101',
name: 'Global Ventures Inc.',
location: {
city: 'New York'
}
};
const guestData = {
id: 'guest-999'
};
// Mendapatkan bahasa pilihan pengguna dengan aman, default ke 'en-GB'
const userLang = userData?.preferences?.language ?? 'en-GB';
console.log(`Bahasa Pengguna: ${userLang}`); // 'en-GB'
// Mendapatkan negara klien, default ke 'Unknown'
const clientCountry = clientData?.location?.country ?? 'Unknown';
console.log(`Negara Klien: ${clientCountry}`); // 'Unknown'
// Mendapatkan nama tampilan tamu, default ke 'Guest'
const guestDisplayName = guestData?.displayName ?? 'Guest';
console.log(`Nama Tampilan Tamu: ${guestDisplayName}`); // 'Guest'
// Mendapatkan kode pos pengguna, default ke 'N/A'
const userPostcode = userData?.address?.postcode ?? 'N/A';
console.log(`Kode Pos Pengguna: ${userPostcode}`); // 'N/A'
// Bagaimana jika string kosong yang eksplisit valid?
const profileWithEmptyBio = {
username: 'coder',
info: { bio: '' }
};
const profileWithNullBio = {
username: 'developer',
info: { bio: null }
};
const bio1 = profileWithEmptyBio?.info?.bio ?? 'Tidak ada bio yang diberikan';
console.log(`Bio 1: '${bio1}'`); // Bio 1: '' (string kosong dipertahankan)
const bio2 = profileWithNullBio?.info?.bio ?? 'Tidak ada bio yang diberikan';
console.log(`Bio 2: '${bio2}'`); // Bio 2: 'Tidak ada bio yang diberikan' (null diganti)
2. Memanggil Metode Secara Kondisional dengan Tindakan Fallback
Anda dapat menggunakan kombinasi ini untuk menjalankan metode jika ada, jika tidak, lakukan tindakan default atau catat pesan.
const logger = {
log: (message) => console.log(`[INFO] ${message}`)
};
const analytics = {}; // Tidak ada metode 'track'
const systemEvent = 'application_start';
// Coba lacak event, jika tidak, catat saja
analytics.track?.(systemEvent, { origin: 'bootstrap' }) ?? logger.log(`Fallback: Tidak dapat melacak event '${systemEvent}'`);
// Diharapkan: [INFO] Fallback: Tidak dapat melacak event 'application_start'
const anotherLogger = {
warn: (msg) => console.warn(`[WARN] ${msg}`),
log: (msg) => console.log(`[LOG] ${msg}`)
};
anotherLogger.track?.('test') ?? anotherLogger.warn('Metode track tidak tersedia.');
// Diharapkan: [WARN] Metode track tidak tersedia.
3. Menangani Data Internasionalisasi (i18n)
Dalam aplikasi global, struktur data i18n bisa rumit, dan terjemahan tertentu mungkin hilang untuk lokal tertentu. Kombinasi ini memastikan mekanisme fallback yang kuat.
const translations = {
'en-US': {
greeting: 'Hello',
messages: {
welcome: 'Welcome!',
error: 'An error occurred.'
}
},
'es-ES': {
greeting: 'Hola',
messages: {
welcome: '¡Bienvenido!',
loading: 'Cargando...'
}
}
};
function getTranslation(locale, keyPath, defaultValue) {
// Memecah keyPath menjadi array properti
const keys = keyPath.split('.');
// Mengakses properti bersarang secara dinamis menggunakan optional chaining
let result = translations[locale];
for (const key of keys) {
result = result?.[key];
}
// Memberikan default jika terjemahan null atau undefined
return result ?? defaultValue;
}
console.log(getTranslation('en-US', 'messages.welcome', 'Fallback Welcome')); // 'Welcome!'
console.log(getTranslation('es-ES', 'messages.welcome', 'Fallback Welcome')); // '¡Bienvenido!'
console.log(getTranslation('es-ES', 'messages.error', 'Fallback Error')); // 'Fallback Error' (error tidak ada di es-ES)
console.log(getTranslation('fr-FR', 'greeting', 'Bonjour')); // 'Bonjour' (lokal fr-FR tidak ada sama sekali)
Contoh ini dengan indah menunjukkan bagaimana ?. memungkinkan navigasi yang aman melalui objek lokal yang berpotensi tidak ada dan kunci pesan bersarang, sementara ?? memastikan bahwa jika terjemahan tertentu hilang, default yang masuk akal disediakan alih-alih undefined.
Kasus Penggunaan Lanjutan dan Pertimbangan
1. Perilaku Short-Circuiting
Penting untuk diingat bahwa optional chaining melakukan short-circuit. Ini berarti bahwa jika sebuah operan dalam rantai dievaluasi menjadi null atau undefined, sisa ekspresi tidak dievaluasi. Ini bisa bermanfaat untuk kinerja dan mencegah efek samping.
let count = 0;
const user = {
name: 'Anna',
getAddress: () => {
count++;
console.log('Mengambil alamat...');
return { city: 'Paris' };
}
};
const admin = null;
// user ada, getAddress dipanggil
console.log(user?.getAddress()?.city); // Output: Mengambil alamat..., lalu 'Paris'
console.log(count); // 1
// admin adalah null, getAddress TIDAK dipanggil
console.log(admin?.getAddress()?.city); // Output: undefined
console.log(count); // Tetap 1 (getAddress tidak dieksekusi)
2. Optional Chaining dengan De-structuring (Aplikasi Hati-hati)
Meskipun Anda tidak dapat secara langsung menggunakan optional chaining dalam *penugasan* de-structuring seperti const { user?.profile } = data;, Anda dapat menggunakannya saat mendefinisikan variabel dari sebuah objek dan kemudian memberikan fallback, atau dengan melakukan de-structuring setelah mengakses properti dengan aman.
const apiResponse = {
success: true,
payload: {
data: {
user: {
id: 'u456',
name: 'David',
email: 'david@example.com'
}
}
}
};
const emptyResponse = {
success: false
};
// Mengekstrak data yang bersarang dalam dengan default
const userId = apiResponse?.payload?.data?.user?.id ?? 'guest';
const userName = apiResponse?.payload?.data?.user?.name ?? 'Anonymous';
console.log(`User ID: ${userId}, Nama: ${userName}`); // User ID: u456, Nama: David
const guestId = emptyResponse?.payload?.data?.user?.id ?? 'guest';
const guestName = emptyResponse?.payload?.data?.user?.name ?? 'Anonymous';
console.log(`Guest ID: ${guestId}, Nama: ${guestName}`); // Guest ID: guest, Nama: Anonymous
// Pola umum adalah mengakses objek dengan aman terlebih dahulu, lalu de-structure jika ada:
const { user: userDataFromResponse } = apiResponse.payload.data;
const { id = 'default-id', name = 'Default Name' } = userDataFromResponse ?? {};
console.log(`Destructured ID: ${id}, Nama: ${name}`); // Destructured ID: u456, Nama: David
// Untuk respons kosong:
const { user: userDataFromEmptyResponse } = emptyResponse.payload?.data ?? {}; // Gunakan optional chaining untuk payload.data, lalu ?? {} untuk user
const { id: emptyId = 'default-id', name: emptyName = 'Default Name' } = userDataFromEmptyResponse ?? {};
console.log(`Destructured Empty ID: ${emptyId}, Nama: ${emptyName}`); // Destructured Empty ID: default-id, Nama: Default Name
3. Prioritas Operator dan Pengelompokan
Optional chaining (?.) memiliki prioritas lebih tinggi daripada nullish coalescing (??). Ini berarti a?.b ?? c diinterpretasikan sebagai (a?.b) ?? c, yang biasanya merupakan perilaku yang diinginkan. Anda biasanya tidak memerlukan tanda kurung tambahan untuk kombinasi ini.
const config = {
value: null
};
// Dievaluasi dengan benar menjadi (config?.value) ?? 'default'
const result = config?.value ?? 'default';
console.log(result); // 'default'
// Jika nilainya adalah 0:
const configWithZero = {
value: 0
};
const resultZero = configWithZero?.value ?? 'default';
console.log(resultZero); // 0 (karena 0 bukan nullish)
4. Integrasi dengan Pengecekan Tipe (misalnya, TypeScript)
Bagi pengembang yang menggunakan TypeScript, operator optional chaining dan nullish coalescing didukung sepenuhnya dan meningkatkan keamanan tipe. TypeScript dapat memanfaatkan operator ini untuk menyimpulkan tipe dengan benar, mengurangi kebutuhan akan pemeriksaan null eksplisit dalam skenario tertentu dan membuat sistem tipe menjadi lebih kuat.
// Contoh dalam TypeScript (konseptual, bukan JS yang dapat dijalankan)
interface User {
id: string;
name: string;
email?: string; // email bersifat opsional
address?: {
street: string;
city: string;
zipCode?: string; // zipCode bersifat opsional
};
}
function getUserEmail(user: User): string {
// TypeScript memahami user.email bisa jadi undefined, dan menanganinya dengan ??
return user.email ?? 'Tidak ada email yang diberikan';
}
function getUserZipCode(user: User): string {
// TypeScript memahami address dan zipCode bersifat opsional
return user.address?.zipCode ?? 'N/A';
}
const user1: User = { id: '1', name: 'John Doe', email: 'john@example.com', address: { street: 'Main', city: 'Town' } };
const user2: User = { id: '2', name: 'Jane Doe' }; // Tidak ada email atau alamat
console.log(getUserEmail(user1)); // 'john@example.com'
console.log(getUserEmail(user2)); // 'Tidak ada email yang diberikan'
console.log(getUserZipCode(user1)); // 'N/A' (zipCode tidak ada)
console.log(getUserZipCode(user2)); // 'N/A' (address tidak ada)
Integrasi ini menyederhanakan pengembangan, karena kompiler membantu Anda memastikan bahwa semua jalur opsional dan nullish ditangani dengan benar, yang selanjutnya mengurangi error saat runtime.
Praktik Terbaik dan Perspektif Global
Mengadopsi optional chaining dan nullish coalescing secara efektif melibatkan lebih dari sekadar memahami sintaksnya; ini memerlukan pendekatan strategis terhadap penanganan data dan desain kode, terutama untuk aplikasi yang melayani audiens global.
1. Kenali Data Anda
Selalu berusaha untuk memahami struktur potensial dari data Anda, terutama dari sumber eksternal. Meskipun ?. dan ?? menawarkan keamanan, mereka tidak menggantikan kebutuhan akan kontrak data yang jelas atau dokumentasi API. Gunakan mereka ketika sebuah field *diharapkan* bersifat opsional atau bisa hilang, bukan sebagai solusi umum untuk skema data yang tidak diketahui.
2. Seimbangkan Keringkasan dengan Keterbacaan
Meskipun operator ini membuat kode lebih pendek, rantai yang terlalu panjang masih bisa menjadi sulit dibaca. Pertimbangkan untuk memecah jalur akses yang sangat dalam atau membuat variabel perantara jika itu meningkatkan kejelasan.
// Berpotensi kurang mudah dibaca:
const userCity = clientRequest?.customer?.billing?.primaryAddress?.location?.city?.toUpperCase() ?? 'UNKNOWN';
// Pemecahan yang lebih mudah dibaca:
const primaryAddress = clientRequest?.customer?.billing?.primaryAddress;
const userCity = primaryAddress?.location?.city?.toUpperCase() ?? 'UNKNOWN';
3. Bedakan Antara 'Hilang' dan 'Secara Eksplisit Kosong/Nol'
Di sinilah ?? benar-benar bersinar. Untuk formulir internasional atau entri data, pengguna mungkin secara eksplisit memasukkan '0' untuk kuantitas, 'false' untuk pengaturan boolean, atau string kosong '' untuk komentar opsional. Ini adalah input yang valid dan tidak boleh diganti dengan nilai default. ?? memastikan presisi ini, tidak seperti || yang akan memperlakukannya sebagai pemicu untuk default.
4. Penanganan Error: Masih Penting
Optional chaining mencegah TypeError untuk akses null/undefined, tetapi tidak mencegah jenis error lain (misalnya, error jaringan, argumen fungsi yang tidak valid, error logika). Aplikasi yang kuat masih memerlukan strategi penanganan error yang komprehensif seperti blok try...catch untuk potensi masalah lainnya.
5. Pertimbangkan Dukungan Browser/Lingkungan
Optional chaining dan nullish coalescing adalah fitur JavaScript modern (ES2020). Meskipun didukung secara luas di browser kontemporer dan versi Node.js, jika Anda menargetkan lingkungan yang lebih tua, Anda mungkin perlu mentranspilasi kode Anda menggunakan alat seperti Babel. Selalu periksa statistik browser audiens target Anda untuk memastikan kompatibilitas atau merencanakan transpilasi.
6. Perspektif Global tentang Default
Saat memberikan nilai default, pertimbangkan audiens global Anda. Sebagai contoh:
- Tanggal dan Waktu: Memberikan default ke zona waktu atau format tertentu harus memperhatikan lokasi pengguna.
- Mata Uang: Mata uang default (misalnya, USD) mungkin tidak sesuai untuk semua pengguna.
- Bahasa: Selalu sediakan bahasa fallback yang masuk akal (misalnya, Bahasa Inggris) jika terjemahan lokal tertentu hilang.
- Satuan Pengukuran: Memberikan default ke 'metrik' atau 'imperial' harus sadar konteks.
Operator ini memudahkan untuk mengimplementasikan default yang sadar konteks seperti itu dengan elegan.
Kesimpulan
Operator Optional Chaining (?.) dan Nullish Coalescing (??) dari JavaScript adalah alat yang sangat diperlukan bagi setiap pengembang modern. Mereka memberikan solusi yang elegan, ringkas, dan kuat untuk masalah umum yang terkait dengan penanganan data yang berpotensi hilang atau tidak terdefinisi dalam struktur objek yang kompleks.
Dengan memanfaatkan optional chaining, Anda dapat dengan aman menavigasi jalur properti yang dalam dan memanggil metode tanpa takut akan TypeError yang merusak aplikasi. Dengan mengintegrasikan nullish coalescing, Anda mendapatkan kontrol yang tepat atas nilai default, memastikan bahwa hanya nilai null atau undefined yang benar-benar diganti, sementara nilai falsy yang sah seperti 0 atau false dipertahankan.
Bersama-sama, "pasangan kuat" ini secara drastis meningkatkan keterbacaan kode, mengurangi boilerplate, dan mengarah pada aplikasi yang lebih tangguh yang menangani sifat data dunia nyata yang tidak dapat diprediksi di berbagai lingkungan global. Menerapkan fitur-fitur ini adalah langkah yang jelas menuju penulisan kode JavaScript yang lebih bersih, lebih mudah dipelihara, dan sangat profesional. Mulailah mengintegrasikannya ke dalam proyek Anda hari ini dan rasakan perbedaan yang mereka buat dalam membangun aplikasi yang benar-benar kuat untuk pengguna di seluruh dunia!