Bekerja dengan Ketiadaan di JavaScript: Variabel dan Fungsi
Seperti banyak bahasa pemrograman lain, JavaScript mengenal indikator ketiadaan, yaitu nilai null
dan undefined
. Baik null
maupun undefined
adalah nilai eksklusif dari tipe data masing-masing, seperti yang pernah kita bahas di Berbicara Null, Undefined, Not Defined, dan Undeclared. Mudahnya, undefined
adalah nilai yang dihasilkan sistem, sementara null
adalah nilai yang disematkan secara sadar oleh pemrogram.
Meskipun pada dasarnya berbeda, null
dan undefined
memiliki banyak kemiripan. Keduanya sama-sama mengindikasikan ketiadaan; keduanya sama-sama nilai eksklusif dari tipe datanya masing-masing; dan keduanya sama-sama bersifat falsy. Karena kemiripan-kemiripan tersebut, kedua nilai tersebut kemudian diklasifikasikan sebagai nullish values.
Terdapat beberapa hal yang sebaiknya diperhatikan ketika bekerja dengan nilai-nilai nullish.
Nilai Nullish dan Variabel
Ketika sebuah variabel dideklarasikan tanpa nilai, maka nilainya adalah undefined
. Meskipun pada umumnya tidak masalah, namun akan lebih baik jika pemrogram mendeklarasikan variabel dengan nilai null
jika memang variabel sengaja dideklarasikan tanpa nilai.
let sesuatu; // hindari
let something = null; // utamakan
Selain karena keterbacaannya lebih baik, perilaku type coercion mereka juga berbeda. Ketika melakukan operasi yang memiliki ekspektasi evaluasi nilai bertipe number, undefined
akan di-coerce menjadi NaN
, sementara null
menjadi 0
.
let x;
let y = null;
console.log(1 + x); // NaN
console.log(1 + y); // 1
Kemudian perlu perhatikan juga, selagi variabel yang dideklarasikan tanpa nilai akan memiliki nilai undefined
, variabel yang tidak pernah dideklarasikan... well, tidak eksis sama sekali.
Jika Anda melakukan pengecekan sederhana dengan if(foo)
yang bergantung pada coercion undefined
menjadi false
, Anda akan menemukan ReferenceError
jika foo
belum dideklarasikan. Dalam keadaan Anda tidak yakin apakah variabel yang akan dicek sudah dideklarasikan, operator typeof
bisa digunakan.
Operator typeof
selalu mengembalikan nilai string, bahkan untuk variabel yang belum dideklarasikan, di mana dalam kasus tersebut ia akan mengembalikan "undefined"
.
if (foo) {
// hindari!
/* selain akan melempar ReferenceError jika foo
** belum dideklarasikan, pengecekan ini tidak meloloskan
** nilai-nilai falsy seperti 0, '', null
*/
}
if (typeof foo !== 'undefined') {
// utamakan!
/* hanya tidak meloloskan dua kasus: foo bernilai
** undefined, atau foo belum dideklarasikan
*/
}
Nilai Nullish dan Fungsi
Fungsi memperlakukan null
dan undefined
secara berbeda. Jika Anda memiliki sebuah fungsi yang menerima suatu parameter, memanggilnya tanpa memberikan argumen untuk parameter tersebut akan membuat nilai variabel parameter tersebut di dalam fungsi menjadi undefined
.
function foo(val) {
console.log(val);
}
foo(); // undefined
Jika kita perhatikan, variabel parameter selalu terdeklarasikan di dalam fungsi. Anda tidak akan mengalami ReferenceError
jika Anda melakukan pengecekan if (val)
di dalam fungsi tersebut. Namun, pilihan paling akurat untuk pengecekan undefined
tetap dengan menggunakan typeof val === 'undefined'
.
Bagaimana jika parameter fungsi kita memiliki nilai default? Ini adalah salah satu gotcha yang pernah membuat saya tersandung. Ternyata, sebagaimanapun miripnya null
dan undefined
, mereka tetap hal yang berbeda, mari kita simak.
function foo(val = 42) {
return val;
}
foo(); // 42
foo(undefined); // 42
foo(null); // null
Memberikan nilai null
tidak akan membuat parameter fungsi diberikan nilai default, tidak seperti undefined
. Mengapa begitu? Sederhananya kembali ke definisi null
di atas. Nilai null
adalah nilai yang diberikan pemrogram untuk mengindikasikan ketiadaan nilai. Jadi, keberadaan nilai null
dianggap intentional dan tidak memerlukan penggantian dengan nilai default.
Bicara soal intentional, bukankah ketika kita mengirimkan argumen undefined
, itu juga dilakukan dengan sengaja? Kalau tidak, mengapa kita repot-repot melakukan foo(undefined)
padahal foo()
saja cukup?
Jawabannya, bisa saja keberadaan undefined
sebagai argumen terjadi bukan secara sengaja dengan mengirimkan nilai primitif undefined
. Contohnya seperti di bawah ini.
const book = {
title: 'Moby Dick',
author: 'Herman Melville',
price: 100000
};
function calculateFinalPrice(basePrice, discount = 0) {
return basePrice * (discount / 100);
}
function addToCart(book) {
const finalPrice = calculateFinalPrice(book.price, book.discount);
// ...
}
Pemanggilan calculateFinalPrice
di dalam fungsi addToCart
memang secara sengaja mengirimkan argumen kedua book.discount
, yang mengarah ke parameter discount
pada calculateFinalPrice
. Namun, objek book
ternyata tidak memiliki properti discount
sehingga nilainya menjadi undefined
. Dalam kasus ini, tentu masuk akal jika nilai default mengambilalih argumen book.discount
yang dikirimkan, bukan?
Oke, berarti memanggil fungsi tanpa argumen sama saja dengan memanggilnya dengan argumen undefined
? Tidak juga :). Memanggil fungsi dengan argumen undefined
secara eksplisit dapat dibedakan dari memanggil fungsi tanpa argumen apapun.
Setiap fungsi dalam JavaScript memiliki objek arguments
, sebuah objek Array-like berisi argumen-argumen yang diberikan kepada fungsi tersebut. Disebut Array-like karena memiliki kemiripan dengan Array dalam hal memiliki length
, tetapi tidak memiliki semua properti Array. Melalui properti arguments.length
tersebut kita bisa mengidentifikasi perbedaan pemanggilan foo()
dan foo(undefined)
.
function foo() {
console.log(arguments.length);
}
foo(); // 0
foo(undefined); // 1
foo(undefined, undefined, undefined); // 3
Kenapa perbedaan itu perlu diperhatikan? Mari kita ilustrasikan. Sejak ES6, kita bisa menggunakan rest parameters untuk merepresentasikan argumen-argumen tanpa batas jumlah yang dikirimkan ke fungsi. Dengan menggunakan rest parameters, argumen-argumen tersebut praktis menjadi sebuah Array dengan berbagai metode prototipenya.
function printArgs(...args) {
args.forEach(arg => {
console.log(arg);
})
}
printArgs(); // loop tidak berjalan sama sekali
printArgs(undefined, undefined, undefined); // loop berjalan 3 kali
Pengetahuan tersebut mungkin akan berguna jika Anda membuat fungsi dengan panjang parameter yang dinamis, dan argumen yang dikirimkan kepadanya dapat memiliki nilai undefined
.
Demikian ulasan tentang nullish values, null
dan undefined
, dalam kaitannya dengan variabel dan fungsi di JavaScript. Dari bahasan di atas, sepertinya lebih banyak perbedaannya, ya? Lalu kenapa null
dan undefined
dikelompokkan menjadi satu jika memiliki banyak perbedaan?
Jawabannya akan terlihat pada bahasan kita selanjutnya, Bekerja dengan Ketiadaan dalam Javascript: Objek dan Operator Tanda Tanya.