Setiap nilai yang kita simpan ke variabel pada chapter sebelumnya pasti memiliki tipe: teks, angka, boolean, dan seterusnya. JavaScript adalah bahasa bertipe dinamis — kita tidak menuliskan tipe saat deklarasi, dan satu variabel boleh berganti jenis nilai sepanjang program berjalan. Kelenturan ini mempercepat penulisan kode, tetapi juga membuka pintu bagi bug konversi yang halus, apalagi di Node.js yang sering menerima masukan mentah dari argumen CLI, berkas, dan jaringan yang semuanya berupa string. Pada chapter ini kita akan memetakan tipe data JavaScript, memeriksa tipe dengan typeof, menguasai konversi eksplisit, lalu membedakan perbandingan longgar == dengan perbandingan ketat ===.
Kategori Tipe Data #
Tipe data JavaScript terbagi dalam dua kategori besar. Kategori pertama adalah primitive: nilai yang tunggal dan tidak bisa diubah (immutable) — string, number, bigint, boolean, undefined, symbol, plus null. Kategori kedua adalah reference: object, yang mencakup object biasa, array, function, Date, Map, dan Set. Perbedaan praktisnya: nilai primitive dibandingkan berdasarkan isinya, sedangkan nilai reference dibandingkan berdasarkan alamat memori yang ditunjuknya.
| Aspek | Primitive | Reference |
|---|---|---|
| Contoh | "Budi", 42, true, null | {}, [], function |
| Disimpan | nilai langsung di variable | referensi ke lokasi memori |
Perbandingan === | membandingkan isi | membandingkan referensi |
| Bisa diubah isinya | tidak (selalu dibuat nilai baru) | ya, property bisa dimodifikasi |
Satu jenis number menampung bilangan bulat dan pecahan sekaligus — tidak ada tipe int vs float seperti di Java atau C. Untuk angka yang melebihi batas aman Number.MAX_SAFE_INTEGER, kita memakai bigint yang ditulis dengan akhiran n, misalnya 9007199254740993n.
Memeriksa Tipe dengan typeof #
Operator typeof mengembalikan string yang menyatakan jenis sebuah nilai. Operator ini adalah alat pemeriksaan tipe yang paling sering kita pakai saat memvalidasi masukan dari sumber yang tidak terkontrol. Simpan berikut sebagai typeof-demo.mjs lalu jalankan dengan node typeof-demo.mjs:
console.log(`typeof "Node.js" : ${typeof "Node.js"}`);
console.log(`typeof 42 : ${typeof 42}`);
console.log(`typeof 3.14 : ${typeof 3.14}`);
console.log(`typeof true : ${typeof true}`);
console.log(`typeof undefined : ${typeof undefined}`);
console.log(`typeof null : ${typeof null}`);
console.log(`typeof 10n : ${typeof 10n}`);
console.log(`typeof Symbol("id"): ${typeof Symbol("id")}`);
console.log(`typeof console.log : ${typeof console.log}`);
console.log(`typeof {} : ${typeof {}}`);
console.log(`typeof [] : ${typeof []}`);
console.log(`Array.isArray([]) : ${Array.isArray([])}`);Output yang Dihasilkan:
typeof "Node.js" : string
typeof 42 : number
typeof 3.14 : number
typeof true : boolean
typeof undefined : undefined
typeof null : object
typeof 10n : bigint
typeof Symbol("id"): symbol
typeof console.log : function
typeof {} : object
typeof [] : object
Array.isArray([]) : trueDua hasil pada output di atas perlu perhatian khusus:
typeof nullmenghasilkan"object"— bukan"null". Perilaku ini adalah warisan implementasi awal JavaScript yang dipertahankan demi kompatibilitas; cara yang benar mengujinulladalah perbandingan ketatnilai === null.typeof []juga menghasilkan"object"karena array memang turunan object. Untuk menguji array, gunakanArray.isArray()seperti pada baris terakhir.
Perhatikan pula bahwa typeof console.log menghasilkan "function" — function juga warga kategori reference, tetapi typeof memberi jawaban yang lebih spesifik untuk function sehingga kita bisa membedakannya dari object biasa.
Konversi Nilai Secara Eksplisit #
Konversi eksplisit (type casting) terjadi ketika kita sengaja memanggil Number(), String(), atau Boolean() untuk mengubah jenis nilai. Konversi ini wajib dikuasai karena hampir semua data mentah di Node.js datang sebagai string. Berikut isi konversi.mjs:
// Konversi eksplisit ke number
console.log(`Number("42") : ${Number("42")}`);
console.log(`Number("3.5") : ${Number("3.5")}`);
console.log(`Number("") : ${Number("")}`);
console.log(`Number("12px") : ${Number("12px")}`);
console.log(`parseInt("12px") : ${parseInt("12px")}`);
console.log(`Number(true) : ${Number(true)}`);
// Konversi eksplisit ke string
console.log(`String(42) : ${String(42)}`);
console.log(`String(null) : ${String(null)}`);
console.log(`String(false) : ${String(false)}`);
// Konversi eksplisit ke boolean
console.log(`Boolean("") : ${Boolean("")}`);
console.log(`Boolean(0) : ${Boolean(0)}`);
console.log(`Boolean("0") : ${Boolean("0")}`);
console.log(`Boolean(null) : ${Boolean(null)}`);
console.log(`Boolean("teks") : ${Boolean("teks")}`);
console.log(`!!"Node.js" : ${!!"Node.js"}`);Output yang Dihasilkan:
Number("42") : 42
Number("3.5") : 3.5
Number("") : 0
Number("12px") : NaN
parseInt("12px") : 12
Number(true) : 1
String(42) : 42
String(null) : null
String(false) : false
Boolean("") : false
Boolean(0) : false
Boolean("0") : true
Boolean(null) : false
Boolean("teks") : true
!!"Node.js" : trueMari kita bedah hasil-hasil pentingnya:
Number("")menghasilkan0, bukan error — string kosong dianggap nol. Aturan yang sama berlaku untuk string berisi spasi.Number("12px")menghasilkanNaN(Not a Number) karenaNumber()menuntut seluruh string berupa angka, sedangkanparseInt("12px")berhenti di karakter pertama yang bukan digit dan tetap mengembalikan12.Boolean("0")menghasilkantrue— string"0"tidak kosong, jadi truthy. Daftar nilai falsy pendek dan tetap:false,0,-0,0n,"",null,undefined, danNaN.!!nilaiadalah singkatan idiomatik dua kali negasi: negasi pertama mengubah nilai ke boolean, negasi kedua mengembalikan makna aslinya.
Perbandingan Longgar dan Ketat #
Operator == melakukan perbandingan longgar: bila kedua operand bertipe berbeda, JavaScript mengonversi salah satunya lebih dulu menurut aturan abstract equality. Operator === melakukan perbandingan ketat: tipe berbeda langsung dinyatakan tidak sama, tanpa konversi apa pun. Kode Node.js modern memakai === secara konsisten supaya hasil perbandingan selalu bisa diprediksi. Perhatikan perbandingan.mjs:
const nomorVersi = "1";
const angka = 1;
console.log(`"1" == 1 : ${nomorVersi == angka}`);
console.log(`"1" === 1 : ${nomorVersi === angka}`);
console.log(`null == undefined : ${null == undefined}`);
console.log(`null === undefined : ${null === undefined}`);
console.log(`NaN === NaN : ${NaN === NaN}`);
console.log(`Number.isNaN(NaN) : ${Number.isNaN(NaN)}`);
// Operator aritmetika memicu konversi implisit yang berbeda-beda
console.log(`"42" + 1 : ${"42" + 1}`);
console.log(`"42" - 1 : ${"42" - 1}`);Output yang Dihasilkan:
"1" == 1 : true
"1" === 1 : false
null == undefined : true
null === undefined : false
NaN === NaN : false
Number.isNaN(NaN) : true
"42" + 1 : 421
"42" - 1 : 41Tiga pasangan hasil pada output menunjukkan sifat kedua operator:
"1" == 1bernilaitruekarena==mengonversi string"1"menjadi number1sebelum membandingkan;===menolak konversi sehingga hasilnyafalse.null == undefinedadalah satu-satunya pasangan lintas tipe yang dianggap sama oleh==; dengan===, keduanya berbeda.NaN === NaNbernilaifalsekarena NaN didefinisikan tidak sama dengan nilai mana pun, termasuk dirinya sendiri — cara mengujinya adalahNumber.isNaN().
null dan undefined #
Kedua nilai ini sama-sama berarti “kosong” tetapi berasal dari sumber berbeda. undefined dipakai JavaScript sendiri: variabel yang dideklarasikan tanpa nilai, property yang tidak ada, dan hasil function tanpa return semuanya bernilai undefined. null dipakai programmer untuk menyatakan “sengaja dikosongkan” — misalnya kolom email yang memang tidak diisi pengguna. Perhatikan perbedaannya pada snippet berikut:
let alamat;
const pengguna = { nama: "Budi", email: null };
console.log(`telepon (tidak ada) : ${pengguna.telepon}`);
console.log(`email (dikosongkan) : ${pengguna.email}`);
console.log(`alamat (belum diisi): ${alamat}`);Output yang Dihasilkan:
telepon (tidak ada) : undefined
email (dikosongkan) : null
alamat (belum diisi): undefinedProperty telepon tidak pernah dideklarasikan pada object pengguna, sehingga JavaScript menjawab undefined. Property email sengaja diisi null oleh pembuat object — pembaca kode tahu bahwa field tersebut sudah dipertimbangkan dan memang dikosongkan. Variabel alamat baru dideklarasikan tanpa nilai, jadi masih undefined.
Kasus Nyata di Node.js: Argumen CLI #
Sudah saatnya kita lihat konversi tipe bekerja dalam konteks Node.js yang sesungguhnya. Semua argumen yang dikirim lewat process.argv datang sebagai string, apa pun bentuk tulisannya di terminal — angka 7 pun tiba sebagai "7". Berkas argumen-cli.mjs membaca satu argumen lalu mengonversinya:
// Semua argumen CLI datang sebagai string
const masukan = process.argv[2];
const jumlah = Number(masukan);
console.log(`masukan mentah : ${masukan} (typeof ${typeof masukan})`);
console.log(`setelah Number : ${jumlah} (typeof ${typeof jumlah})`);
console.log(`angka valid : ${!Number.isNaN(jumlah)}`);Output yang Dihasilkan:
masukan mentah : 7 (typeof string)
setelah Number : 7 (typeof number)
angka valid : trueProgram dijalankan dengan perintah node argumen-cli.mjs 7. Baris pertama membaca elemen process.argv indeks ke-2 — elemen indeks 0 dan 1 berisi path ke executable node dan path berkas skrip. Pemeriksaan Number.isNaN() pada baris terakhir adalah pola pertahanan standar: bila pengguna mengetik node argumen-cli.mjs tujuh, konversi menghasilkan NaN dan program bisa menolak masukan itu dengan rapi alih-alih menghasilkan perhitungan yang salah.
Referensi lanjutan untuk topik chapter ini:
Kini kita bisa menyimpan nilai dengan kata kunci yang tepat dan memahami tipe yang dimiliki nilai-nilai tersebut. Selanjutnya, nilai-nilai itu perlu diolah melalui operasi yang bisa dipakai ulang — di sanalah function berperan, mulai dari function declaration klasik sampai arrow function modern. Lanjut ke Chapter Function dan Arrow Function.