Di chapter sebelumnya kita membuktikan pencarian properti bisa melompat satu tingkat: blender tidak punya sapa, sehingga engine bertanya ke dasarAlat. Pertanyaan yang wajar muncul sekarang — dan pernah dialami Siti saat menata data pesanan kafenya — adalah: kalau object di tingkat atas pun tidak punya propertinya, apakah engine berhenti, terus mencari, atau malah menimbulkan galat?
Jawabannya adalah mekanisme bernama prototype chain: tautan [[Prototype]] membentuk rantai, dan pencarian properti menyusurinya sampai properti ditemukan atau rantai benar-benar tamat. Chapter ini menelusuri rantai itu tingkat demi tingkat, lalu membandingkan dua operator yang sering tertukar saat memeriksa properti: in dan hasOwnProperty.
Alur Pencarian Properti #
Aturannya satu kalimat: ketika membaca objek.properti, engine memeriksa objek sendiri dulu; bila tidak ditemukan, engine naik ke [[Prototype]]-nya, lalu ke [[Prototype]] berikutnya, dan seterusnya sampai rantai mencapai null.
Ujung rantai selalu sama untuk object biasa: Object.prototype, lalu null. Ketika pencarian tiba di null, engine berhenti dan melaporkan hasilnya sebagai undefined — bukan galat. Inilah alasan membaca properti yang tidak ada di mana pun tidak membuat program berhenti; yang datang kembali hanyalah undefined.
Alur lengkapnya untuk pencarian pesanan.kurir terlihat seperti ini:
Diagram itu pula yang menjelaskan temuan chapter lalu: toString ditemukan di Object.prototype, dua tingkat di atas object kecil seperti produk. Semakin jauh properti berada di rantai, semakin banyak tingkat yang harus disusuri — detail performa yang jarang terasa, tetapi baik dipahami sejak awal.
Aturan “berhenti di null” punya dua wajah yang perlu dibedakan. Wajah pertama ramah: membaca properti yang tidak ada di mana pun menghasilkan undefined, dan program berlanjut seperti biasa. Wajah kedua muncul satu baris kemudian, ketika undefined itu kita pakai seperti object — misalnya membaca properti darinya. Kesalahan tunggal di data berubah menjadi TypeError di baris pemakaian:
const pesanan = {};
try {
console.log(pesanan.kurir.panjang);
} catch (err) {
console.log(`${err.name}: ${err.message}`);
}
console.log("Program tetap berjalan");Output yang Dihasilkan:
TypeError: Cannot read properties of undefined (reading 'panjang')
Program tetap berjalanPesan galat itu sudah kita kenal dari section Error Handling, dan kini pemicunya bisa kita baca dengan kerangka prototype chain: pencarian kurir tiba di null, hasilnya undefined, lalu .panjang mencoba dibaca dari undefined yang memang tidak bisa menampung apa pun. Karena try...catch menangkapnya, program tetap mencetak baris terakhir — kegagalan satu jalur tidak meruntuhkan semuanya.
Menelusuri Rantai dalam Satu Berkas #
Berkas berikut merakit rantai dua tingkat — pesanan di ujung bawah, dasarPesanan di tengah, Object.prototype di puncak — lalu membaca berbagai properti untuk melihat hasil pencarian di tiap tingkat:
// jejak-pencarian.js
// Membaca properti = menelusuri rantai prototype sampai ketemu atau berhenti di null.
const dasarPesanan = {
ringkas() {
return `${this.nomor}: ${this.item} x${this.jumlah}`;
},
};
const pesanan = Object.create(dasarPesanan);
pesanan.nomor = "P-101";
pesanan.item = "Kopi Susu";
pesanan.jumlah = 2;
console.log(pesanan.nomor);
console.log(pesanan.ringkas());
console.log(pesanan.valueOf);
console.log(pesanan.kurir);
console.log(Object.getPrototypeOf(pesanan) === dasarPesanan);
console.log(Object.getPrototypeOf(dasarPesanan) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
console.log("nomor" in pesanan);
console.log("ringkas" in pesanan);
console.log("kurir" in pesanan);
console.log(pesanan.hasOwnProperty("nomor"));
console.log(pesanan.hasOwnProperty("ringkas"));
console.log(Object.hasOwn(pesanan, "nomor"));
for (const kunci in pesanan) {
console.log(kunci);
}Output yang Dihasilkan:
P-101
P-101: Kopi Susu x2
[Function: valueOf]
undefined
true
true
true
true
true
false
true
false
true
nomor
item
jumlah
ringkasMari kita baca baris demi baris:
- Baris 4–8 (
dasarPesanan): object tengah rantai dengan satu methodringkas. Method ini membacathis.nomordan kawan-kawannya — nilai yang kelak diambil daripesanandi bawahnya. - Baris 10–13 (
Object.create+ tiga penugasan):pesanandibuat dengan [[Prototype]] menunjuk kedasarPesanan, lalu menerima tiga properti sendiri. - Baris 15 (
pesanan.nomor): properti sendiri — pencarian selesai di tingkat pertama. - Baris 16 (
pesanan.ringkas()): method tidak ada dipesanan, ditemukan satu tingkat di atas, dan berjalan denganthismenunjuk kepesanansehingga mencetak data pesanan. - Baris 17 (
pesanan.valueOf): tidak ada dipesanandan tidak ada didasarPesanan; baru diObject.prototypefunctionvalueOfditemukan — bukti pencarian dua tingkat sekaligus. - Baris 18 (
pesanan.kurir): tidak ada di seluruh rantai; pencarian mencapainulldan hasilnyaundefined, tanpa galat apa pun. - Baris 20–22 (tiga
getPrototypeOf): rantainya terbaca satu per satu —pesananmenunjukdasarPesanan,dasarPesananmenunjukObject.prototype, danObject.prototypemenunjuknull. Titiknullitulah akhir dari semua rantai. - Baris 24–26 (operator
in):inmemeriksa seluruh rantai, sehingganomordanringkasmenjawabtrue, sedangkankurirmenjawabfalse. - Baris 28–29 (
hasOwnProperty): pemeriksaan ini hanya menyentuh object itu sendiri —nomoradalah milikpesanan(true), sedangkanringkasyang berada satu tingkat di atas dilaporkanfalse. - Baris 30 (
Object.hasOwn): versi modern dengan perilaku sama sepertihasOwnProperty, direkomendasikan untuk kode baru. - Baris 32–34 (
for...in): loop ini menelusuri rantai dan menghitung semua kunci yang dapat dihitung — tiga properti sendiri ditambahringkasdari prototype. Method bawaan sepertivalueOftidak ikut karena tidak dapat dihitung.
in, hasOwnProperty, dan Object.hasOwn #
Tiga alat pemeriksaan itu menjawab pertanyaan yang berbeda, dan perbedaannya persis mengikuti jangkauan pencariannya:
| Alat | Jangkauan | Pertanyaan yang dijawab |
|---|---|---|
objek.hasOwnProperty(kunci) | Object itu sendiri | “Apakah properti ini milikmu sendiri?” |
Object.hasOwn(objek, kunci) | Object itu sendiri | Sama dengan di atas, gaya modern |
"kunci" in objek | Seluruh rantai | “Apakah properti ini bisa dijangkau?” |
Aturan memilihnya sederhana. Untuk memastikan data milik object itu sendiri — misalnya membedakan pesanan yang benar-benar membawa resi dari pesanan yang hanya mewarisinya — gunakan Object.hasOwn. Untuk kebutuhan “apakah bisa dipakai”, termasuk method warisan, operator in lebih pas.
Satu Pengecualian: Object Tanpa Prototype #
Perintah Object.create yang kita pakai sepanjang chapter ini sebenarnya bisa menerima satu nilai khusus: null. Hasilnya object yang rantainya benar-benar kosong — tidak ada Object.prototype, tidak ada toString, tidak ada apa pun di atasnya:
const tasKunci = Object.create(null);
tasKunci.dari = "Siti";
console.log(Object.getPrototypeOf(tasKunci));
console.log(tasKunci.toString);Output yang Dihasilkan:
null
undefinedObject seperti ini disebut prototype-less dan berguna sebagai tas kunci-nilai murni: pencarian properti tidak akan pernah terbawa ke method bawaan, dan data yang kita simpan tidak bisa bertabrakan dengan nama method seperti toString. Akses data juga sedikit lebih cepat karena rantainya pendek. Sisi lainnya wajar diikuti: tanpa prototype, object kehilangan semua kemampuan bawaan — tasKunci.toString adalah undefined nyata, dan memanggilnya akan gagal.
Rantai prototype menjelaskan dari mana method warisan datang, tetapi cara kita merakit object berantai selama ini masih manual lewat Object.create. Chapter berikutnya memperkenalkan pola yang jauh lebih umum di kode nyata: constructor function, yang memanggil new dan menautkan prototype secara otomatis.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
jejak-pencarian.js
Unduh berkas// jejak-pencarian.js
// Membaca properti = menelusuri rantai prototype sampai ketemu atau berhenti di null.
const dasarPesanan = {
ringkas() {
return `${this.nomor}: ${this.item} x${this.jumlah}`;
},
};
const pesanan = Object.create(dasarPesanan);
pesanan.nomor = "P-101";
pesanan.item = "Kopi Susu";
pesanan.jumlah = 2;
console.log(pesanan.nomor);
console.log(pesanan.ringkas());
console.log(pesanan.valueOf);
console.log(pesanan.kurir);
console.log(Object.getPrototypeOf(pesanan) === dasarPesanan);
console.log(Object.getPrototypeOf(dasarPesanan) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
console.log("nomor" in pesanan);
console.log("ringkas" in pesanan);
console.log("kurir" in pesanan);
console.log(pesanan.hasOwnProperty("nomor"));
console.log(pesanan.hasOwnProperty("ringkas"));
console.log(Object.hasOwn(pesanan, "nomor"));
for (const kunci in pesanan) {
console.log(kunci);
}