Saya mencoba memahami di balik layar tirai Javascript dan agak terjebak dalam memahami penciptaan objek bawaan, khususnya Obyek dan Fungsi dan hubungan di antara mereka.
Ini rumit, mudah disalahpahami, dan banyak buku Javascript pemula yang salah, jadi jangan percaya semua yang Anda baca.
Saya adalah salah satu pelaksana mesin JS Microsoft pada 1990-an dan pada komite standardisasi, dan saya membuat sejumlah kesalahan dalam menyusun jawaban ini. (Meskipun karena saya belum mengerjakan ini selama lebih dari 15 tahun saya mungkin bisa dimaafkan.) Itu adalah hal yang rumit. Tapi begitu Anda memahami warisan prototipe, semuanya masuk akal.
Ketika saya membaca bahwa semua objek bawaan seperti Array, String dll adalah ekstensi (diwarisi) dari Object, saya berasumsi bahwa Object adalah objek built-in pertama yang dibuat dan sisa objek yang diwarisi darinya.
Mulailah dengan membuang semua yang Anda ketahui tentang warisan berbasis kelas. JS menggunakan pewarisan berbasis prototipe.
Selanjutnya, pastikan Anda memiliki definisi yang sangat jelas di kepala Anda tentang apa arti "warisan". Orang yang terbiasa dengan bahasa OO seperti C # atau Java atau C ++ berpikir bahwa pewarisan berarti subtipe, tetapi pewarisan tidak berarti subtipe. Warisan berarti bahwa anggota dari satu hal juga anggota dari hal lain . Itu tidak selalu berarti bahwa ada hubungan subtyping antara hal-hal itu! Begitu banyak kesalahpahaman dalam teori jenis adalah hasil dari orang tidak menyadari bahwa ada perbedaan.
Tapi itu tidak masuk akal ketika Anda mengetahui bahwa Objek hanya dapat dibuat oleh fungsi tetapi kemudian fungsi juga tidak lain adalah objek Fungsi.
Ini benar-benar salah. Beberapa objek tidak dibuat dengan memanggil new F
beberapa fungsi F
. Beberapa objek dibuat oleh runtime JS dari ketiadaan sama sekali. Ada telur yang tidak diletakkan oleh ayam apa pun . Mereka baru saja dibuat oleh runtime saat dijalankan.
Katakanlah apa aturannya dan mungkin itu akan membantu.
- Setiap instance objek memiliki objek prototipe.
- Dalam beberapa kasus, prototipe dapat dibuat
null
.
- Jika Anda mengakses anggota pada instance objek, dan objek tidak memiliki anggota tersebut, maka objek tersebut akan menolak prototipenya, atau berhenti jika prototipenya nol.
- The
prototype
anggota dari suatu objek biasanya tidak prototipe dari objek.
- Sebaliknya,
prototype
anggota objek fungsi F adalah objek yang akan menjadi prototipe objek yang dibuat olehnew F()
.
- Dalam beberapa implementasi, contoh mendapatkan
__proto__
anggota yang benar-benar memberikan prototipe mereka. (Ini sekarang sudah usang. Jangan mengandalkan itu.)
- Objek fungsi mendapatkan objek default baru yang ditetapkan
prototype
saat dibuat.
- Prototipe objek fungsi tentu saja
Function.prototype
.
Mari kita simpulkan.
- Prototipe dari
Object
adalahFunction.prototype
Object.prototype
adalah objek prototipe objek.
- Prototipe dari
Object.prototype
adalahnull
- Prototipe
Function
is Function.prototype
- ini adalah salah satu situasi langka di mana Function.prototype
sebenarnya adalah prototipe Function
!
Function.prototype
adalah objek prototipe fungsi.
- Prototipe dari
Function.prototype
adalahObject.prototype
Misalkan kita membuat fungsi Foo.
- Prototipe dari
Foo
adalah Function.prototype
.
Foo.prototype
adalah objek prototipe Foo.
- Prototipe dari
Foo.prototype
adalah Object.prototype
.
Anggap saja kita katakan new Foo()
- Prototipe objek baru tersebut adalah
Foo.prototype
Pastikan itu masuk akal. Mari kita menggambarnya. Oval adalah instance objek. Tepi bisa __proto__
berarti "prototipe", atau prototype
berarti " prototype
properti".
Yang harus dilakukan runtime adalah membuat semua objek itu dan menetapkan berbagai propertinya sesuai. Saya yakin Anda bisa melihat bagaimana itu akan dilakukan.
Sekarang mari kita lihat contoh yang menguji pengetahuan Anda.
function Car(){ }
var honda = new Car();
print(honda instanceof Car);
print(honda.constructor == Car);
Apa yang dicetak ini?
Nah, apa instanceof
artinya? honda instanceof Car
berarti " Car.prototype
sama dengan benda apa pun honda
di rantai prototipe?"
Ya itu. honda
Prototipenya sudah Car.prototype
, jadi kita selesai. Ini mencetak benar.
Bagaimana dengan yang kedua?
honda.constructor
tidak ada jadi kami berkonsultasi dengan prototipe, yaitu Car.prototype
. Ketika Car.prototype
objek itu dibuat secara otomatis diberi properti constructor
sama dengan Car
, jadi ini benar.
Sekarang bagaimana dengan ini?
var Animal = new Object();
function Reptile(){ }
Reptile.prototype = Animal;
var lizard = new Reptile();
print(lizard instanceof Reptile);
print(lizard.constructor == Reptile);
Apa yang dicetak oleh program ini?
Sekali lagi, lizard instanceof Reptile
berarti " Reptile.prototype
sama dengan objek apa pun dilizard
di rantai prototipe?"
Ya itu. lizard
prototipe adalahReptile.prototype
, jadi kita selesai. Ini mencetak benar.
Sekarang bagaimana
print(lizard.constructor == Reptile);
Anda mungkin berpikir bahwa ini juga mencetak benar, karena lizard
dibangun dengan new Reptile
tetapi Anda akan salah. Alasan itu keluar.
- Apakah
lizard
punyaconstructor
properti? Tidak. Oleh karena itu kami melihat prototipe.
- Prototipe dari
lizard
adalah Reptile.prototype
, yaituAnimal
.
- Apakah
Animal
punyaconstructor
properti? Tidak. Jadi kita melihat prototipe itu.
- Prototipe dari
Animal
adalah Object.prototype
, dan Object.prototype.constructor
dibuat oleh runtime dan sama dengan Object
.
- Jadi ini mencetak salah.
Kita seharusnya mengatakan Reptile.prototype.constructor = Reptile;
pada suatu titik di sana, tetapi kita tidak ingat untuk melakukannya!
Pastikan semua masuk akal bagi Anda. Gambarlah beberapa kotak dan panah jika masih membingungkan.
Hal lain yang sangat membingungkan adalah, jika saya console.log(Function.prototype)
mencetak fungsi tetapi ketika saya mencetaknya console.log(Object.prototype)
mencetak objek. Mengapa Function.prototype
suatu fungsi saat itu dimaksudkan untuk menjadi objek?
Prototipe fungsi didefinisikan sebagai fungsi yang, ketika dipanggil, kembali undefined
. Kita sudah tahu bahwa Function.prototype
adalah Function
prototipe, anehnya. Jadi karena Function.prototype()
itu legal, dan ketika Anda melakukannya, Anda undefined
kembali. Jadi itu sebuah fungsi.
The Object
prototipe tidak memiliki properti ini; itu tidak bisa dipanggil. Itu hanya sebuah objek.
ketika Anda console.log(Function.prototype.constructor)
lagi fungsi.
Function.prototype.constructor
hanya Function
, jelas. Dan Function
merupakan fungsi.
Sekarang bagaimana Anda dapat menggunakan sesuatu untuk membuatnya sendiri (Mind = blown).
Anda terlalu memikirkan ini . Semua yang diperlukan adalah bahwa runtime membuat banyak objek saat dijalankan. Objek hanyalah tabel pencarian yang mengaitkan string dengan objek. Ketika runtime dijalankan, itu harus lakukan adalah membuat beberapa objek lusin kosong, dan kemudian mulai menempatkan prototype
, __proto__
, constructor
, dan sebagainya properti dari setiap objek sampai mereka membuat grafik yang mereka butuhkan untuk membuat.
Akan sangat membantu jika Anda mengambil diagram yang saya berikan di atas dan menambahkan constructor
pinggirannya. Anda akan segera melihat bahwa ini adalah grafik objek yang sangat sederhana dan runtime tidak akan bermasalah saat membuatnya.
Latihan yang baik adalah melakukannya sendiri. Di sini, saya akan memulai Anda. Kami akan menggunakan my__proto__
berarti "objek prototipe" dan myprototype
berarti "properti prototipe".
var myobjectprototype = new Object();
var myfunctionprototype = new Object();
myfunctionprototype.my__proto__ = myobjectprototype;
var myobject = new Object();
myobject.myprototype = myobjectprototype;
Dan seterusnya. Bisakah Anda mengisi sisa program untuk membangun satu set objek yang memiliki topologi yang sama dengan objek bawaan Javascript "asli"? Jika Anda melakukannya, Anda akan menemukannya sangat mudah.
Objek dalam JavaScript hanyalah tabel pencarian yang mengaitkan string dengan objek lain . Itu dia! Tidak ada keajaiban di sini. Anda mendapatkan diri Anda terikat di simpul karena Anda membayangkan kendala yang sebenarnya tidak ada, seperti setiap objek harus dibuat oleh konstruktor.
Fungsi hanyalah objek yang memiliki kemampuan tambahan: untuk dipanggil. Jadi, ikuti program simulasi kecil Anda dan tambahkan .mycallable
properti ke setiap objek yang menunjukkan apakah itu dapat dipanggil atau tidak. Sesederhana itu.
Function.prototype
bisa menjadi fungsi dan memiliki bidang dalam. Jadi tidak, Anda tidak menjalankan fungsi prototipe ketika melalui struktur itu. Akhirnya ingat bahwa ada mesin yang menafsirkan Javascript, jadi Obyek dan Fungsi mungkin dibuat di dalam mesin dan bukan dari Javascript dan referensi khusus sepertiFunction.prototype
danObject.prototype
mungkin hanya ditafsirkan dengan cara khusus oleh mesin.