Ketika Budi menulis object produk untuk katalog kafenya, ia hanya memasang dua properti: nama dan stok. Lalu suatu hari ia iseng mengetik produk.toString — dan alih-alih undefined, yang muncul adalah sebuah function. Padahal jelas ia tidak pernah menulis toString di dalam produk. Properti itu datang dari mana?
Pertanyaan Budi adalah pintu masuk ke konsep paling fundamental dari OOP JavaScript: prototype. Setiap object membawa tautan tersembunyi ke object lain, dan lewat tautan itu object bisa memakai properti yang tidak pernah ditulis padanya. Chapter ini mengajak kita memeriksa tautan tersebut, membandingkan dua cara pemeriksaan yang tersedia, dan membuktikan bahwa properti benar-benar bisa diwarisi antar object.
Aturan: Setiap Object Punya Prototype #
Aturannya begini: setiap object yang kita buat membawa tautan internal bernama [[Prototype]] yang menunjuk ke object lain. Ketika kita membaca sebuah properti, engine JavaScript memeriksa object itu dulu; bila propertinya tidak ditemukan, engine melanjutkan pencarian ke object di ujung tautan [[Prototype]].
Itulah mekanisme di balik temuan Budi. Object produk tidak memiliki toString, tetapi object di ujung tautannya punya. Engine tidak menyerah begitu properti tidak ditemukan di produk — pencarian dilanjutkan satu tingkat ke atas:
const produk = {
nama: "Kopi Gayo",
stok: 24,
};
console.log(produk.nama);
console.log(produk.toString);Output yang Dihasilkan:
Kopi Gayo
[Function: toString]Mari kita telaah:
- Baris 2–5 (
const produk = {...}): object biasa dengan dua properti sendiri,namadanstok. Tidak ada satu pun method yang kita tulis. - Baris 7 (
console.log(produk.nama)): properti ditemukan langsung diproduk, sehingga nilainya tercetak tanpa pencarian tambahan. - Baris 8 (
console.log(produk.toString)):produktidak punyatoString, sehingga engine melompat ke [[Prototype]]-nya dan menemukan function di sana.[Function: toString]adalah bukti tautan itu bekerja.
Satu hal penting: [[Prototype]] adalah internal slot — sebuah jalur data di dalam engine, bukan properti biasa. Kita tidak bisa mengetik produk.[[Prototype]] untuk membacanya, karena sintaks tersebut tidak dikenal JavaScript. Karena itulah bahasa ini menyediakan dua cara resmi untuk mengintip tautan tersebut.
Object.getPrototypeOf dan proto #
JavaScript menyediakan dua cara memeriksa [[Prototype]] sebuah object. Cara modern dan direkomendasikan adalah Object.getPrototypeOf(objek). Cara klasik adalah membaca properti objek.__proto__ — sebuah getter yang tersedia di semua object, tetapi statusnya sudah lama ditandai usang untuk mengubah prototype.
| Aspek | Object.getPrototypeOf(o) | o.__proto__ |
|---|---|---|
| Bentuk | Function utilitas milik Object | Getter yang diwarisi semua object |
| Status | Standar resmi, direkomendasikan | Bekerja, tetapi ditandai usang |
| Mengubah prototype | Gunakan Object.setPrototypeOf | Bisa, tetapi tidak disarankan |
| Kegunaan praktis | Pemeriksaan di kode production | Pemeriksaan cepat, eksplorasi di console |
Untuk membaca dan membandingkan — kebutuhan paling umum — keduanya memberi hasil yang sama. Berkas berikut membuktikannya langsung:
// pemeriksa-prototype.js
// Mengintip [[Prototype]]: sumber properti yang tidak kita tulis sendiri.
const produk = {
nama: "Kopi Gayo",
stok: 24,
};
console.log(produk.nama);
console.log(produk.stok);
console.log(produk.toString);
const protoProduk = Object.getPrototypeOf(produk);
console.log(protoProduk === Object.prototype);
console.log(produk.__proto__ === Object.prototype);
console.log(produk.__proto__ === protoProduk);Output yang Dihasilkan:
Kopi Gayo
24
[Function: toString]
true
true
trueBedah baris-baris pentingnya:
- Baris 11 (
console.log(produk.toString)): function warisan tadi, kini tercetak sebagai bagian dari eksperimen utuh. - Baris 13–14 (
Object.getPrototypeOf(produk)): function utilitas mengembalikan object di ujung tautan [[Prototype]]. Perbandingan=== Object.prototypemenghasilkantrue— object literal biasa diwariskan langsung dariObject.prototype. - Baris 16 (
produk.__proto__ === Object.prototype): getter klasik menunjuk ke object yang sama, sehingga perbandingannya jugatrue. - Baris 17 (
produk.__proto__ === protoProduk): kedua cara mengembalikan object yang identik, dibuktikan dengan===yang membandingkan identitas, bukan isi.
Dua Nama Tampak Serupa: [[Prototype]] dan .prototype #
Sampai titik ini dua istilah sering tercampur di kepala pemula, dan memisahkannya sekarang akan menyelamatkan kita dari kebingungan di chapter-chapter berikutnya. Yang pertama, [[Prototype]], adalah tautan internal yang dimiliki setiap object — jalur yang dilalui engine saat mencari properti. Yang kedua, .prototype, adalah properti biasa milik function dan class — isinya sebuah object yang kelak dijadikan [[Prototype]] untuk semua instance hasil new.
Perbedaannya bisa dibuktikan dalam empat baris:
function KartuMember() {}
console.log(typeof KartuMember.prototype);
const produk = {};
console.log(produk.prototype);Output yang Dihasilkan:
object
undefinedFunction memiliki properti .prototype yang isinya object — inilah cetakan tautan untuk instance-nya. Sebaliknya, object biasa seperti produk tidak punya properti bernama prototype sama sekali; yang dimiliki produk adalah [[Prototype]] yang hanya bisa diintip lewat Object.getPrototypeOf atau __proto__. Untuk memudahkan kembali ke pembedaannya:
| Istilah | Milik siapa | Isinya | Perannya |
|---|---|---|---|
[[Prototype]] | Setiap object | Tautan ke object lain | Jalur pencarian properti |
.prototype | Function dan class | Sebuah object | Dijadikan [[Prototype]] bagi hasil new |
Satu kecil tapi membantu: KartuMember.prototype adalah object biasa, dan ia sendiri punya [[Prototype]] — menunjuk ke Object.prototype. Rantai yang terbentuk itulah topik utuh chapter berikutnya.
Properti Warisan dengan Object.create #
Bukti paling gamblang bahwa properti bisa diwariskan: kita buat sendiri object yang berperan sebagai prototype, lalu kita buat object lain darinya. Function Object.create(proto) membuat object baru dengan [[Prototype]] yang menunjuk ke proto:
const dasarAlat = {
sapa(nama) {
return `Halo ${nama}, alat siap dipakai.`;
},
};
const blender = Object.create(dasarAlat);
blender.merek = "Kencana";
console.log(blender.merek);
console.log(blender.sapa("Budi"));
console.log(blender.sapa === dasarAlat.sapa);
console.log(Object.getPrototypeOf(blender) === dasarAlat);Output yang Dihasilkan:
Kencana
Halo Budi, alat siap dipakai.
true
trueObject blender hanya punya satu properti sendiri, yaitu merek. Method sapa ditulis hanya di dasarAlat, tetapi blender.sapa("Budi") tetap berjalan — engine tidak menemukan sapa di blender, lalu menemukannya satu tingkat di atas. Perbandingan blender.sapa === dasarAlat.sapa menghasilkan true: kedua nama menunjuk function yang sama, bukan salinan yang berbeda. Inilah pola berbagi yang kelak kita andalkan di chapter constructor function.
Kita sudah membuktikan pencarian satu tingkat ke atas. Tanda tanya yang tersisa bisa diuji sekarang: apa yang dikembalikan bila properti tidak ada di object mana pun sepanjang tautan?
const alatDapur = Object.create({ garansi: "12 bulan" });
console.log(alatDapur.garansi);
console.log(alatDapur.jaminan);Output yang Dihasilkan:
12 bulan
undefinedgaransi ditemukan di prototype yang kita tentukan sendiri, sedangkan jaminan tidak ada di alatDapur maupun di sepanjang tautannya — dan hasilnya undefined tanpa galat. Pertanyaan “sampai mana tautan itu berhenti” adalah inti dari prototype chain, dan itulah peta yang akan kita susun di chapter berikutnya.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
pemeriksa-prototype.js
Unduh berkas// pemeriksa-prototype.js
// Mengintip [[Prototype]]: sumber properti yang tidak kita tulis sendiri.
const produk = {
nama: "Kopi Gayo",
stok: 24,
};
console.log(produk.nama);
console.log(produk.stok);
console.log(produk.toString);
const protoProduk = Object.getPrototypeOf(produk);
console.log(protoProduk === Object.prototype);
console.log(produk.__proto__ === Object.prototype);
console.log(produk.__proto__ === protoProduk);