Kamu sudah kenal bentuk dasar sebuah objek: kurung kurawal, beberapa nilai berlabel di dalamnya, dan dot notation untuk membacanya. Bekal segitu memang cukup untuk mulai, dan itu sudah kita bahas di artikel array dan objek. Tapi objek adalah tulang punggung hampir semua yang kamu bangun di JavaScript, dan begitu kamu mulai menulis kode betulan, muncul pertanyaan-pertanyaan yang nggak terjawab oleh materi dasar. Gimana caranya membaca key yang namanya baru ketahuan saat program berjalan? Kenapa menyalin objek kadang nggak benar-benar menyalin? Gimana cara me-looping isi sebuah objek? Lalu, apa artinya kalau sebuah objek punya method?
Artikel ini mengupas semua itu. Setelah selesai, pemahamanmu soal objek bakal cukup matang untuk membaca kode orang lain tanpa pusing, sekaligus menghindari segelintir jebakan halus yang hampir pasti menjerat tiap orang pada suatu titik.
Dua cara membaca dan menulis property
Dot notation adalah cara sehari-hari untuk mengakses isi objek, dan inilah yang sebaiknya jadi pilihan utamamu karena terbaca rapi:
const pengguna = {
nama: "John Doe",
umur: 28,
};
console.log(pengguna.nama); // "John Doe"
pengguna.umur = 29; // ubah nilai
pengguna.kota = "Jakarta"; // tambah property baru
Tapi ada cara kedua: bracket notation, ditulis dengan kurung siku dan key berupa string. Hasilnya sama persis — sampai pada titik tertentu, karena kurung siku bisa melakukan sesuatu yang dot notation jelas nggak bisa.
console.log(pengguna["nama"]); // "John Doe" — sama dengan pengguna.nama
Sejauh ini memang kelihatan cuma lebih panjang. Bedanya muncul karena yang ada di dalam kurung siku itu sebuah ekspresi. Isinya bisa berupa variabel, yang artinya kamu bisa membaca property yang namanya baru kamu ketahui ketika program berjalan:
const field = "umur";
console.log(pengguna[field]); // 28 — membaca property "umur"
// Ini beda dengan dot notation yang sifatnya harfiah:
console.log(pengguna.field); // undefined — mencari key yang namanya persis "field"
Di sinilah letak perbedaan yang penting. pengguna.field mencari property yang namanya benar-benar field. Sementara pengguna[field] mengevaluasi field dulu (jadi string "umur") lalu mencari hasil itu. Kapan pun key-nya datang dari variabel, input pengguna, atau perulangan, kamu butuh kurung siku.
Saat kamu wajib pakai kurung siku
Bracket notation bukan cuma soal variabel — ini juga satu-satunya cara menjangkau key yang bukan identifier yang valid. Key yang mengandung spasi atau tanda hubung ({ "nama depan": "John" }), atau key yang murni angka, nggak bisa ditulis pakai titik: pengguna.nama depan langsung jadi syntax error. Mau nggak mau kamu harus menulis pengguna["nama depan"]. Jadi patokan gampangnya begini: pakai dot notation sebagai default karena lebih enak dibaca, dan beralih ke kurung siku saat key-nya dinamis atau punya nama yang nggak biasa.
Computed property name
Kurung siku juga berlaku saat kamu menulis objek literal, bukan cuma saat membacanya. Membungkus sebuah key dengan kurung siku di dalam { } memungkinkan kamu menghitung nama key dari sebuah variabel — ini namanya computed property name:
const key = "status";
const order = {
id: 101,
[key]: "dikirim", // key-nya jadi "status"
};
console.log(order.status); // "dikirim"
Tanpa kurung siku, key: "dikirim" bakal membuat property yang namanya benar-benar key. Dengan kurung siku, JavaScript mengevaluasi variabelnya dulu lalu memakai nilainya sebagai nama property. Pola ini bakal sering kamu lihat di kode yang membangun objek secara dinamis — misalnya saat mengubah sederet field menjadi sebuah objek.
Shorthand property
Kalau nama sebuah variabel sama dengan nama key yang kamu inginkan, ES2015 mengizinkanmu melewati pengulangan. Daripada menulis nama: nama, kamu cukup menulis nama sekali:
const nama = "Jane Doe";
const umur = 34;
// Cara panjang:
const penggunaA = { nama: nama, umur: umur };
// Shorthand — hasilnya identik:
const penggunaB = { nama, umur };
console.log(penggunaB); // { nama: "Jane Doe", umur: 34 }
Ini murni soal kepraktisan, tapi kamu bakal menemuinya di mana-mana pada kode modern, terutama saat sebuah fungsi mengembalikan objek. Pola ini pas banget dengan fungsi yang mengumpulkan beberapa nilai lalu mengembalikannya dalam bentuk objek yang ringkas.
Method: fungsi yang tinggal di dalam objek
Nilai sebuah property bisa apa saja — number, string, array, bahkan objek lain. Nilai itu juga bisa berupa fungsi. Fungsi yang disimpan sebagai property dari sebuah objek disebut method, dan method adalah cara objek melakukan sesuatu, bukan sekadar menyimpan sesuatu.
const kalkulator = {
total: 0,
tambah(jumlah) {
this.total += jumlah;
},
reset() {
this.total = 0;
},
};
kalkulator.tambah(50);
kalkulator.tambah(30);
console.log(kalkulator.total); // 80
Penulisan tambah(jumlah) { ... } adalah bentuk shorthand modern untuk mendefinisikan method (sama saja dengan tambah: function(jumlah) { ... }, cuma lebih ringkas). Cara memanggil method persis seperti yang kamu bayangkan: objek.method(argumen).
Keyword this di dalam method
Perhatikan this.total di contoh tadi. Di dalam sebuah method, this merujuk ke objek tempat method itu dipanggil — jadi this.total berarti “property total dari objek yang aku jadi method-nya.” Begitulah tambah tahu total yang mana yang harus diperbarui.
const akun = {
pemilik: "John Doe",
saldo: 1000,
deskripsi() {
return `${this.pemilik} punya saldo ${this.saldo}`;
},
};
console.log(akun.deskripsi()); // "John Doe punya saldo 1000"
this adalah salah satu sudut JavaScript yang paling bikin pusing, dan perilakunya sepenuhnya bergantung pada bagaimana sebuah fungsi dipanggil, bukan di mana ia didefinisikan. Cerita lengkapnya layak dapat artikel sendiri, tapi untuk method biasa yang dipanggil sebagai objek.method(), aturannya sederhana: this adalah objek yang ada sebelum tanda titik.
Arrow function nggak punya this sendiri
Ada satu jebakan yang perlu ditandai sejak sekarang. Kalau kamu mendefinisikan method memakai arrow function, ia nggak akan mengikat this ke objeknya — arrow function justru mewarisi this dari scope di sekelilingnya. Jadi deskripsi: () => this.pemilik di dalam objek biasanya bakal memberimu undefined, bukan nama pemilik. Untuk method yang butuh this menunjuk ke objeknya, pakai bentuk shorthand deskripsi() { ... }, jangan arrow. Simpan arrow untuk callback yang memang kamu inginkan tetap memegang this dari luar.
Objek disalin berdasarkan referensi, bukan nilai
Ini hal paling penting — sekaligus paling mengejutkan — soal objek, dan inilah yang paling sering bikin orang bingung di seluruh daftar ini. Saat kamu menugaskan sebuah objek ke variabel baru, kamu tidak mendapat salinan objek itu. Yang kamu dapat adalah nama kedua yang menunjuk ke objek yang sama.
const asli = { jumlah: 1 };
const salinan = asli; // BUKAN salinan — objek yang sama, dua nama
salinan.jumlah = 99;
console.log(asli.jumlah); // 99 — yang asli ikut berubah!
Baik asli maupun salinan adalah label yang menempel pada satu objek yang sama di baliknya. Ubah lewat label mana pun, perubahannya muncul lewat keduanya. Inilah yang dimaksud “objek disimpan berdasarkan referensi”, dan ini benar-benar beda dari cara kerja tipe primitif (number, string, boolean) — yang justru disalin berdasarkan nilainya:
let a = 5;
let b = a; // b adalah salinan asli yang mandiri
b = 10;
console.log(a); // 5 — nggak terpengaruh
Perilaku referensi yang sama juga berlaku saat kamu mengoper objek ke dalam sebuah fungsi: fungsi itu menerima referensi ke objek yang sama, sehingga perubahan yang ia lakukan terlihat oleh pemanggilnya. Array juga bekerja begini, sebab di balik layar array sebenarnya juga objek.
Membuat salinan sungguhan: spread dan Object.assign
Kalau kamu benar-benar mau salinan yang mandiri, kamu harus memintanya secara eksplisit. Cara modern yang paling bersih adalah operator spread (...), yang menyalin semua property sebuah objek ke dalam objek yang baru:
const asli = { jumlah: 1, label: "a" };
const salinanAsli = { ...asli };
salinanAsli.jumlah = 99;
console.log(asli.jumlah); // 1 — kali ini nggak tersentuh
console.log(salinanAsli.jumlah); // 99
Object.assign({}, asli) melakukan tugas yang sama, dan kamu masih akan menemuinya di kode lama:
const salinanAsli = Object.assign({}, asli);
Spread juga memudahkanmu menyalin sekaligus mengubah dalam satu langkah, sebuah pola yang super umum (begini cara update state bekerja di framework seperti React):
const diperbarui = { ...asli, jumlah: 2 };
// semua property dari asli, tapi jumlah ditimpa jadi 2
Spread itu salinan dangkal — objek bersarang tetap berbagi
Ini jebakan yang menjerat orang justru setelah mereka belajar soal spread: ia cuma menyalin satu tingkat ke dalam. Kalau sebuah property isinya objek atau array lagi, salinannya bakal memegang referensi yang sama ke objek bersarang itu, bukan salinannya. Jadi const salinan = { ...pengguna } lalu salinan.alamat.kota = "Bandung" juga akan mengubah pengguna.alamat.kota, karena kedua objek menunjuk ke alamat yang sama. Untuk salinan dalam (deep copy) yang benar-benar mandiri, pakai structuredClone(asli) di lingkungan modern, atau fungsi dari sebuah library. Yang penting kamu ingat: { ...obj } dan Object.assign itu dangkal — sempurna untuk objek datar, tapi jebakan untuk objek bersarang.
Me-looping sebuah objek
Array punya index, jadi perulangan for bisa menyusurinya dengan mulus. Objek justru punya key bernama, sehingga mengiterasinya butuh pendekatan berbeda. Tiga method andalan berikut mengubah isi objek menjadi array yang bisa kamu looping dengan alat array yang sudah kamu kuasai.
Object.keys() memberimu array berisi key-nya, Object.values() memberimu nilai-nilainya, dan Object.entries() memberimu keduanya sebagai pasangan [key, value]:
const nilai = {
matematika: 90,
sains: 85,
sejarah: 78,
};
console.log(Object.keys(nilai)); // ["matematika", "sains", "sejarah"]
console.log(Object.values(nilai)); // [90, 85, 78]
console.log(Object.entries(nilai)); // [["matematika", 90], ["sains", 85], ["sejarah", 78]]
Karena masing-masing mengembalikan array sungguhan, kamu bisa merangkainya dengan forEach, map, atau metode array mana pun yang sudah kamu pelajari:
Object.entries(nilai).forEach(([mapel, skor]) => {
console.log(`${mapel}: ${skor}`);
});
// matematika: 90
// sains: 85
// sejarah: 78
Bagian [mapel, skor] di dalam callback itu adalah array destructuring — ia mengeluarkan dua elemen dari tiap pasangan ke variabel bernama. Begitu kamu terbiasa, ini terbaca enak banget.
Perulangan for…in
Ada juga perulangan khusus untuk objek, yaitu for...in, yang mengiterasi key sebuah objek secara langsung. Ia berfungsi, tapi punya sisi tajam yang perlu kamu tahu:
for (const key in nilai) {
console.log(key, nilai[key]);
}
Utamakan Object.keys/entries ketimbang for...in
for...in menyusuri bukan cuma key milik objeknya sendiri, tapi juga key apa pun yang diwarisi dari rantai prototype-nya, yang bisa mengejutkanmu dengan property yang nggak pernah kamu taruh di situ. Object.keys() dan Object.entries() hanya mengembalikan property milik objek itu sendiri, jadi lebih bisa ditebak dan langsung memberimu array normal yang bisa kamu map dan filter. Untuk iterasi objek sehari-hari, andalkan Object.entries(obj).forEach(...) lebih dulu. Kalau suatu saat kamu memang pakai for...in, jaga dengan Object.hasOwn(obj, key) untuk melewati key warisan.
Memeriksa apakah sebuah property ada
Mengakses property yang nggak ada akan mengembalikan undefined, bukan melempar error. Ini memang praktis, tapi bisa menyembunyikan bug. Beberapa cara bersih untuk memeriksa keberadaan property:
const pengguna = { nama: "John Doe", umur: 28 };
console.log("nama" in pengguna); // true — operator `in`
console.log("email" in pengguna); // false
console.log(Object.hasOwn(pengguna, "umur")); // true — modern, khusus property sendiri
console.log(pengguna.email !== undefined); // false — juga berhasil
Operator in singkat dan enak dibaca. Object.hasOwn() adalah pilihan modern yang andal saat kamu memang mau mengabaikan property warisan. Membandingkan terhadap undefined memang berfungsi, tapi sedikit rapuh — property yang isinya benar-benar undefined bakal terbaca seolah “tidak ada”, padahal biasanya bukan itu maksudmu.
Optional chaining: menjangkau objek bersarang dengan aman
Data nyata bersarang dalam-dalam, dan menjangkau lewat serangkaian property jadi berisiko kalau salah satu bagian di tengahnya mungkin nggak ada. Mengakses property pada undefined akan melempar error dan mematikan baris itu:
const order = { id: 1 }; // nggak ada `pelanggan` di sini
// console.log(order.pelanggan.nama); // TypeError: Cannot read properties of undefined
Optional chaining (?.) menyelesaikan ini dengan rapi. Kalau nilai sebelum ?. adalah null atau undefined, seluruh ekspresinya langsung berhenti dan menghasilkan undefined, bukan melempar error:
console.log(order.pelanggan?.nama); // undefined — nggak crash
const lengkap = {
pelanggan: { nama: "Jane Doe", alamat: { kota: "Jakarta" } },
};
console.log(lengkap.pelanggan?.alamat?.kota); // "Jakarta"
Ia berpasangan secara alami dengan operator nullish coalescing (??) untuk menyediakan nilai cadangan saat ada yang hilang:
const kota = order.pelanggan?.alamat?.kota ?? "Tidak diketahui";
console.log(kota); // "Tidak diketahui"
Dua operator ini diam-diam jadi salah satu fitur paling sering dipakai di JavaScript modern, justru karena keduanya membuat pekerjaan dengan data yang berantakan dan terisi separuh-separuh jadi nggak menyakitkan.
Membekukan objek agar tak bisa diubah
Secara default tiap objek itu bisa diubah — siapa pun yang memegang referensinya bisa mengubahnya. Saat kamu ingin mengunci sebuah objek supaya nggak bisa dimodifikasi, Object.freeze() melakukan persis itu:
const config = Object.freeze({
apiUrl: "https://api.example.com",
timeout: 5000,
});
config.timeout = 9000; // diabaikan diam-diam (atau melempar error di strict mode)
console.log(config.timeout); // 5000 — nggak berubah
Ini berguna untuk objek konfigurasi dan konstanta yang nggak ingin kamu sentuh sama sekali. Dua catatan: percobaan mengubahnya gagal diam-diam kecuali kodemu berjalan di strict mode (di situ ia melempar error), dan seperti spread, pembekuan ini sifatnya dangkal — objek bersarang di dalam objek yang dibekukan tetap bisa diubah. Untuk kebanyakan kebutuhan config, freeze yang dangkal sudah lebih dari cukup.
Contoh realistis
Coba kita rangkai semua kepingannya jadi sesuatu yang mendekati kode nyata — sebuah order kecil dengan method, key dinamis, dan akses yang aman:
function buatOrder(pelanggan, items) {
return {
pelanggan, // shorthand property
items,
get jumlahItem() { // accessor terhitung
return this.items.length;
},
total() { // method yang memakai `this`
return this.items.reduce((sum, item) => sum + item.harga, 0);
},
};
}
const order = buatOrder("Jane Doe", [
{ nama: "Keyboard", harga: 250000 },
{ nama: "Mouse", harga: 120000 },
]);
console.log(order.pelanggan); // "Jane Doe"
console.log(order.jumlahItem); // 2
console.log(order.total()); // 370000
// membaca dengan aman sesuatu yang mungkin nggak ada:
console.log(order.kupon?.kode ?? "tanpa kupon"); // "tanpa kupon"
Satu fungsi pabrik ini memakai shorthand property, sebuah method yang bersandar pada this, sebuah getter, dan optional chaining di sisi pemanggilnya — semua ide dari artikel ini bekerja bareng persis seperti di kode sehari-hari.
Sedikit catatan soal bekerja dengan JSON
Objek juga jadi cara kamu menangani data yang datang dari API atau yang disimpan ke storage. Data semacam itu bepergian dalam bentuk JSON — teks yang tampilannya mirip objek JavaScript — dan kamu mengonversinya bolak-balik dengan JSON.parse (teks ke objek) dan JSON.stringify (objek ke teks). Saat kamu perlu memeriksa atau merapikan teks itu, sebuah JSON formatter bikin tumpukan data yang sudah dimampatkan jadi terbaca dalam hitungan detik. Kita bakal membahas JSON secara tuntas di artikelnya sendiri — untuk sekarang, ketahui saja bahwa keterampilan objek yang sedang kamu bangun di sini persis itulah yang bakal kamu pakai begitu data nyata muncul.
Penutup
Kamu sudah melaju jauh melampaui dasar-dasar objek:
- Bracket notation (
obj[key]) membaca property dengan key yang dinamis atau tak biasa; dot notation adalah default yang enak dibaca untuk key yang tetap. - Computed property name (
[key]: value) dan shorthand ({ nama }) membuat penyusunan objek jadi ringkas. - Fungsi yang disimpan di sebuah objek disebut method, dan
thisdi dalamnya menunjuk ke objek tempat ia dipanggil — tapi arrow function nggak mengikatthis, jadi pakai bentuk method shorthand. - Objek dipegang berdasarkan referensi — menugaskannya nggak menyalinnya. Pakai spread (
{ ...obj }) atauObject.assignuntuk salinan dangkal, danstructuredCloneuntuk salinan dalam. - Iterasi pakai
Object.keys/values/entries, yang mengembalikan array sehingga bisa dipakai dengan metode array; utamakan ketiganya ketimbangfor...in. - Periksa keberadaan property dengan
inatauObject.hasOwn, jangkau data bersarang dengan aman lewat?.dan??, serta kunci objek denganObject.freezesaat dibutuhkan.
Objek, berbarengan dengan metode array dari artikel sebelumnya, adalah dua paruh dari cara JavaScript menyimpan dan membentuk data. Dengan bekal referensi, penyalinan, dan iterasi, kamu sudah siap menyambut sintaks yang bikin proses mengeluarkan nilai dari objek dan array jadi nyaris tanpa usaha — yaitu destructuring, yang jadi bahasan berikutnya.