Kenapa ini penting
JavaScript punya tujuh tipe primitif: string, number, bigint, boolean, undefined, null, dan symbol. Semua yang bukan primitif adalah object (termasuk array dan function). Perbedaan ini bukan sekadar teori. Ia menentukan bagaimana data disalin, dibandingkan, dan diubah.
Pernah heran kenapa "halo".toUpperCase() bisa jalan padahal string itu bukan object? Atau kenapa [1, 2] === [1, 2] bernilai false? Atau kenapa str[0] = "X" tidak mengubah apa-apa? Semua jawabannya ada di modul ini. Pemahaman tentang perbandingan nilai (==, ===, Object.is) juga sangat penting, karena salah memilih operator perbandingan adalah sumber bug logika yang klasik.
Konsep inti
Primitif vs object: nilai vs referensi
- Primitif disimpan sebagai nilai. Saat kamu menulis
let b = adenganaberupa angka,bmendapat salinan nilainya. Mengubahbtidak memengaruhia. - Object disimpan sebagai referensi.
const y = xmembuatymenunjuk ke object yang sama.
Ini juga berlaku saat mengirim argumen ke function. Primitif dikirim sebagai salinan; object dikirim sebagai salinan alamat, sehingga function bisa mengubah isi object aslinya.
Wrapper object dan autoboxing
Primitif tidak punya method. Lalu bagaimana "halo".length atau (3.14159).toFixed(2) bisa bekerja? Saat kamu mengakses properti pada primitif, JavaScript sementara membungkusnya dengan wrapper object (String, Number, Boolean), memanggil method-nya, lalu membuang bungkus itu. Proses ini disebut autoboxing. Analogi: kamu meminjam sarung tangan khusus sebentar untuk memegang sesuatu yang panas, lalu mengembalikannya.
Kamu jangan membuat wrapper secara manual dengan new String("x") atau new Number(5). Hasilnya object, bukan primitif, sehingga typeof bernilai "object", perbandingan === gagal, dan new Boolean(false) justru truthy. Memanggil String(x) atau Number(x) tanpa new itu aman dan menghasilkan primitif (itulah konversi eksplisit dari modul 6).
Immutability (tidak bisa diubah)
Semua primitif bersifat immutable. Method string seperti toUpperCase, replace, atau trim tidak pernah mengubah string aslinya; mereka mengembalikan string baru. str[0] = "X" diabaikan (atau melempar TypeError di strict mode). Untuk "mengubah" string, kamu harus membuat string baru dan menyimpannya kembali ke variabel.
Perlu dibedakan: variabel let bisa diganti isinya (reassign), tapi nilai primitif di dalamnya tidak pernah diubah di tempat.
Perbandingan nilai
| Operator | Cara kerja | Contoh menarik |
|---|---|---|
== |
mengonversi tipe dulu (loose equality) | "1" == 1 → true, null == undefined → true |
=== |
tipe dan nilai harus sama (strict equality) | "1" === 1 → false |
Object.is |
seperti ===, kecuali dua kasus khusus |
Object.is(NaN, NaN) → true, Object.is(0, -0) → false |
Gunakan === sebagai default. Satu-satunya pemakaian == yang umum dan masih dianggap wajar adalah x == null, yang bernilai true untuk null maupun undefined.
Untuk object, semua operator di atas membandingkan referensi, bukan isi. Dua object dengan isi sama tetap tidak sama kalau alamatnya berbeda.
NaN adalah satu-satunya nilai yang tidak sama dengan dirinya sendiri (NaN === NaN → false). Itu sebabnya ada Number.isNaN() dan Object.is().
Contoh kode
"use strict";
// 1) typeof untuk semua tipe
console.log(typeof "teks", typeof 42, typeof 10n, typeof true); // → string number bigint boolean
console.log(typeof undefined, typeof Symbol(), typeof {}, typeof []); // → undefined symbol object object
console.log(typeof null); // → object (bug lama JavaScript yang dipertahankan)
console.log(typeof function () {}); // → function
console.log(Array.isArray([])); // → true (cara benar mengecek array)
// 2) Nilai vs referensi
let a = 10;
let b = a;
b = 20;
console.log(a, b); // → 10 20
const x = { skor: 10 };
const y = x;
y.skor = 99;
console.log(x.skor); // → 99
function tambahSatu(angka, objek) {
angka += 1;
objek.nilai += 1;
}
let n = 1;
const o = { nilai: 1 };
tambahSatu(n, o);
console.log(n, o.nilai); // → 1 2 (primitif tetap, isi object berubah)
// 3) Autoboxing: primitif meminjam method dari wrapper
const kata = "javascript";
console.log(kata.length, kata.toUpperCase(), (3.14159).toFixed(2)); // → 10 JAVASCRIPT 3.14
console.log(typeof kata); // → string (tetap primitif)
// 4) Jangan pakai new String / new Boolean
const s1 = "abc";
const s2 = new String("abc");
console.log(typeof s2, s1 === s2, s1 == s2); // → object false true
const palsu = new Boolean(false);
console.log(palsu ? "truthy!" : "falsy"); // → truthy! (object selalu truthy)
// 5) Immutability string
let salam = "halo";
salam.toUpperCase(); // hasil dibuang, salam tidak berubah
console.log(salam); // → halo
salam = salam.toUpperCase(); // simpan string baru ke variabel
console.log(salam); // → HALO
try {
salam[0] = "J"; // strict mode: melempar TypeError
} catch (err) {
console.log(err.constructor.name); // → TypeError
}
// 6) ==, ===, Object.is
console.log("1" == 1, "1" === 1); // → true false
console.log(0 == "", 0 == false); // → true true (membingungkan, hindari ==)
console.log(null == undefined, null === undefined); // → true false
console.log(NaN === NaN, Object.is(NaN, NaN)); // → false true
console.log(0 === -0, Object.is(0, -0)); // → true false
// x == null: pola sah untuk mengecek null ATAU undefined
const cek = (v) => (v == null ? "kosong" : "ada");
console.log(cek(null), cek(undefined), cek(0)); // → kosong kosong ada
// 7) Object dibandingkan berdasarkan referensi
console.log([1, 2] === [1, 2]); // → false
const arr = [1, 2];
const sama = arr;
console.log(arr === sama); // → true
// Bandingkan isi sederhana (untuk data JSON-friendly)
console.log(JSON.stringify([1, 2]) === JSON.stringify([1, 2])); // → true
Kesalahan umum
- Memanggil method string tanpa menyimpan hasilnya.
nama.trim()saja tidak mengubahnama. Tulisnama = nama.trim()atau simpan ke variabel baru. - Memakai
==untuk perbandingan sehari-hari. Aturan konversinya rumit (0 == ""bernilaitrue). Pakai===. - Mengecek
NaNdengan=== NaN. Selalufalse. PakaiNumber.isNaN(x)atauObject.is(x, NaN). - Memakai
typeofuntuk mengecek array atau null.typeof []dantypeof nullsama-sama"object". PakaiArray.isArray(x)danx === null. - Membuat wrapper dengan
new String/new Number/new Boolean. Hasilnya object yang perilakunya aneh. Pakai literal atau panggil tanpanewuntuk konversi.
Latihan
- Mudah, Buat function
jenis(nilai)yang mengembalikan"array"untuk array,"null"untuk null, dan hasiltypeofuntuk lainnya. - Sedang, Diberikan
let kode = " abc-123 ". Ubah variabel itu menjadi"ABC-123"(tanpa spasi, huruf besar) dengan method string, dan buktikan bahwa string lama tidak "dimodifikasi" melainkan diganti. - Menantang, Buat function
samaPersis(a, b)yang mengembalikantruejikaObject.is(a, b), dan untuk dua array satu level, membandingkan panjang dan setiap elemennya denganObject.is.
Lihat solusi
// 1) Mudah
function jenis(nilai) {
if (Array.isArray(nilai)) return "array";
if (nilai === null) return "null";
return typeof nilai;
}
console.log(jenis([]), jenis(null), jenis(5), jenis("a")); // → array null number string
// 2) Sedang
let kode = " abc-123 ";
const lama = kode;
kode = kode.trim().toUpperCase();
console.log(kode); // → ABC-123
console.log(JSON.stringify(lama)); // → " abc-123 " (string lama tetap utuh)
// 3) Menantang
function samaPersis(a, b) {
if (Object.is(a, b)) return true;
if (Array.isArray(a) && Array.isArray(b)) {
return a.length === b.length && a.every((nilai, i) => Object.is(nilai, b[i]));
}
return false;
}
console.log(samaPersis(NaN, NaN)); // → true
console.log(samaPersis([1, NaN], [1, NaN])); // → true
console.log(samaPersis([0], [-0])); // → false
console.log(samaPersis({}, {})); // → false
Ringkasan
- Ada tujuh tipe primitif; primitif disalin sebagai nilai, object sebagai referensi.
- Autoboxing membuat primitif bisa memakai method wrapper tanpa berubah menjadi object.
- Primitif immutable: method string selalu mengembalikan string baru.
- Gunakan
===sebagai default;Object.ismenanganiNaNdan-0dengan benar. - Object, termasuk array, dibandingkan berdasarkan referensi, bukan isi.