Ingat, objek baru dapat dibuat dengan fungsi konstruktor, seperti new F()
.
Jika F.prototype
adalah sebuah objek, maka operator new
menggunakannya untuk menyetel [[Prototype]]
untuk objek baru tersebut.
Harap dicatat:
JavaScript memiliki warisan prototipe sejak awal. Itu adalah salah satu fitur inti bahasa tersebut.
Namun dahulu kala, tidak ada akses langsung ke sana. Satu-satunya hal yang berfungsi dengan baik adalah properti "prototype"
dari fungsi konstruktor, yang dijelaskan dalam bab ini. Jadi masih banyak script yang masih menggunakannya.
Harap dicatat bahwa F.prototype
di sini berarti properti biasa bernama "prototype"
di F
. Kedengarannya mirip dengan istilah "prototipe", tetapi di sini yang kami maksud adalah properti biasa dengan nama ini.
Berikut contohnya:
biarkan hewan = { makan: benar }; fungsi Kelinci(nama) { ini.nama = nama; } Kelinci.prototipe = binatang; biarkan kelinci = kelinci baru("Kelinci Putih"); // kelinci.__proto__ == binatang waspada( kelinci.makan ); // BENAR
Pengaturan Rabbit.prototype = animal
secara harafiah menyatakan sebagai berikut: “Saat new Rabbit
dibuat, tetapkan [[Prototype]]
miliknya ke animal
”.
Itulah gambar yang dihasilkan:
Pada gambar, "prototype"
adalah panah horizontal yang berarti properti biasa, dan [[Prototype]]
adalah panah vertikal yang berarti pewarisan rabbit
dari animal
.
F.prototype
hanya digunakan pada waktu new F
Properti F.prototype
hanya digunakan ketika new F
dipanggil, ia menetapkan [[Prototype]]
dari objek baru.
Jika, setelah pembuatan, properti F.prototype
berubah ( F.prototype = <another object>
), maka objek baru yang dibuat oleh new F
akan memiliki objek lain sebagai [[Prototype]]
, tetapi objek yang sudah ada tetap menggunakan objek lama.
Setiap fungsi memiliki properti "prototype"
meskipun kami tidak menyediakannya.
"prototype"
default adalah objek dengan satu-satunya constructor
properti yang menunjuk kembali ke fungsi itu sendiri.
Seperti ini:
fungsi Kelinci() {} /* prototipe bawaan Rabbit.prototype = { konstruktor: Kelinci }; */
Kita dapat memeriksanya:
fungsi Kelinci() {} // secara default: // Kelinci.prototipe = { konstruktor: Kelinci } alert( Kelinci.prototipe.konstruktor == Kelinci ); // BENAR
Tentu saja, jika kita tidak melakukan apa pun, properti constructor
tersedia untuk semua kelinci melalui [[Prototype]]
:
fungsi Kelinci() {} // secara default: // Kelinci.prototipe = { konstruktor: Kelinci } biarkan kelinci = Kelinci baru(); // mewarisi dari {konstruktor: Kelinci} alert(kelinci.konstruktor == Kelinci); // benar (dari prototipe)
Kita bisa menggunakan properti constructor
untuk membuat objek baru menggunakan konstruktor yang sama dengan yang sudah ada.
Seperti di sini:
fungsi Kelinci(nama) { ini.nama = nama; peringatan(nama); } biarkan kelinci = kelinci baru("Kelinci Putih"); biarkan kelinci2 = kelinci baru.konstruktor("Kelinci Hitam");
Hal ini berguna ketika kita memiliki sebuah objek, tidak mengetahui konstruktor mana yang digunakan untuk objek tersebut (misalnya objek tersebut berasal dari perpustakaan pihak ketiga), dan kita perlu membuat objek lain yang sejenis.
Tapi mungkin hal terpenting tentang "constructor"
adalah…
…JavaScript sendiri tidak memastikan nilai "constructor"
yang tepat.
Ya, itu ada di "prototype"
default untuk fungsi, tapi itu saja. Apa yang terjadi nanti – sepenuhnya ada pada kita.
Khususnya jika kita mengganti prototipe default secara keseluruhan, maka tidak akan ada "constructor"
di dalamnya.
Misalnya:
fungsi Kelinci() {} Kelinci.prototipe = { melompat: benar }; biarkan kelinci = Kelinci baru(); alert(kelinci.konstruktor === Kelinci); // PALSU
Jadi, untuk mempertahankan "constructor"
yang tepat, kita dapat memilih untuk menambah/menghapus properti ke "prototype"
default alih-alih menimpanya secara keseluruhan:
fungsi Kelinci() {} // Tidak sepenuhnya menimpa Rabbit.prototype // tambahkan saja Kelinci.prototipe.jumps = benar // Rabbit.prototype.constructor default dipertahankan
Atau, alternatifnya, buat ulang properti constructor
secara manual:
Kelinci.prototipe = { melompat: benar, konstruktor: Kelinci }; // sekarang konstruktornya juga benar, karena kita menambahkannya
Dalam bab ini kami menjelaskan secara singkat cara menyetel [[Prototype]]
untuk objek yang dibuat melalui fungsi konstruktor. Nanti kita akan melihat pola pemrograman tingkat lanjut yang mengandalkannya.
Semuanya cukup sederhana, hanya beberapa catatan untuk memperjelas:
Properti F.prototype
(jangan salah mengartikannya sebagai [[Prototype]]
) menyetel [[Prototype]]
objek baru ketika new F()
dipanggil.
Nilai F.prototype
harus berupa objek atau null
: nilai lain tidak akan berfungsi.
Properti "prototype"
hanya memiliki efek khusus ketika diatur pada fungsi konstruktor, dan dipanggil dengan new
.
Pada objek biasa, prototype
tidak istimewa:
biarkan pengguna = { nama: "Yohanes", prototipe: "Bla-bla" // tidak ada keajaiban sama sekali };
Secara default semua fungsi memiliki F.prototype = { constructor: F }
, sehingga kita bisa mendapatkan konstruktor suatu objek dengan mengakses properti "constructor"
.
pentingnya: 5
Pada kode di bawah ini kita membuat new Rabbit
, lalu mencoba memodifikasi prototipenya.
Pada awalnya, kami memiliki kode ini:
fungsi Kelinci() {} Kelinci.prototipe = { makan: benar }; biarkan kelinci = Kelinci baru(); waspada( kelinci.makan ); // BENAR
Kami menambahkan satu string lagi (ditekankan). alert
apa yang akan ditampilkan sekarang?
fungsi Kelinci() {} Kelinci.prototipe = { makan: benar }; biarkan kelinci = Kelinci baru(); Kelinci.prototipe = {}; waspada( kelinci.makan ); // ?
…Dan kalau kodenya seperti ini (diganti satu baris)?
fungsi Kelinci() {} Kelinci.prototipe = { makan: benar }; biarkan kelinci = Kelinci baru(); Kelinci.prototipe.eats = false; waspada( kelinci.makan ); // ?
Dan seperti ini (mengganti satu baris)?
fungsi Kelinci() {} Kelinci.prototipe = { makan: benar }; biarkan kelinci = Kelinci baru(); hapus kelinci.eats; waspada( kelinci.makan ); // ?
Varian terakhir:
fungsi Kelinci() {} Kelinci.prototipe = { makan: benar }; biarkan kelinci = Kelinci baru(); hapus Kelinci.prototipe.makan; waspada( kelinci.makan ); // ?
Jawaban:
true
.
Penugasan ke Rabbit.prototype
menyiapkan [[Prototype]]
untuk objek baru, namun tidak mempengaruhi objek yang sudah ada.
false
.
Objek ditetapkan berdasarkan referensi. Objek dari Rabbit.prototype
tidak diduplikasi, ia masih berupa objek tunggal yang direferensikan oleh Rabbit.prototype
dan oleh [[Prototype]]
dari rabbit
.
Jadi ketika kita mengubah isinya melalui satu referensi, maka akan terlihat melalui referensi lainnya.
true
.
Semua operasi delete
diterapkan langsung ke objek. Di sini delete rabbit.eats
mencoba menghapus properti eats
dari rabbit
, tetapi tidak ada. Jadi operasinya tidak akan berpengaruh apa pun.
undefined
.
Properti eats
dihapus dari prototipe, tidak ada lagi.
pentingnya: 5
Bayangkan, kita mempunyai objek sembarang obj
, yang dibuat oleh fungsi konstruktor – kita tidak tahu yang mana, tapi kita ingin membuat objek baru dengan menggunakannya.
Bisakah kita melakukannya seperti itu?
biarkan obj2 = obj.constructor();
Berikan contoh fungsi konstruktor untuk obj
yang memungkinkan kode tersebut berfungsi dengan baik. Dan contoh yang membuatnya salah berfungsi.
Kita dapat menggunakan pendekatan tersebut jika kita yakin bahwa properti "constructor"
memiliki nilai yang benar.
Misalnya, jika kita tidak menyentuh "prototype"
default, maka kode ini pasti berfungsi:
fungsi Pengguna(nama) { ini.nama = nama; } biarkan pengguna = Pengguna baru('John'); biarkan pengguna2 = pengguna baru.konstruktor('Pete'); peringatan(pengguna2.nama); // Pete (bekerja!)
Berhasil, karena User.prototype.constructor == User
.
…Tetapi jika seseorang, boleh dikatakan, menimpa User.prototype
dan lupa membuat ulang constructor
untuk mereferensikan User
, maka itu akan gagal.
Misalnya:
fungsi Pengguna(nama) { ini.nama = nama; } Pengguna.prototipe = {}; // (*) biarkan pengguna = Pengguna baru('John'); biarkan pengguna2 = pengguna baru.konstruktor('Pete'); peringatan(pengguna2.nama); // belum diartikan
Mengapa user2.name
undefined
?
Begini cara new user.constructor('Pete')
bekerja:
Pertama, ia mencari constructor
di user
. Tidak ada apa-apa.
Kemudian mengikuti rantai prototipe. Prototipe user
adalah User.prototype
, dan juga tidak memiliki constructor
(karena kita “lupa” mengaturnya dengan benar!).
Lebih jauh lagi, User.prototype
adalah objek biasa, prototipenya adalah Object.prototype
bawaan.
Terakhir, untuk Object.prototype
bawaan, ada Object.prototype.constructor == Object
bawaan. Jadi itu digunakan.
Terakhir, pada akhirnya, kita telah let user2 = new Object('Pete')
.
Mungkin bukan itu yang kita inginkan. Kami ingin membuat new User
, bukan new Object
. Itulah hasil dari constructor
yang hilang.
(Kalau-kalau Anda penasaran, panggilan new Object(...)
mengubah argumennya menjadi sebuah objek. Itu adalah hal teoretis, dalam praktiknya tidak ada yang memanggil new Object
dengan nilai, dan umumnya kita tidak menggunakan new Object
untuk membuat objek sama sekali).