Di bagian tentang pewarisan dalam artikel MDN Pengantar Javascript Berorientasi Objek , saya perhatikan mereka mengatur prototype.constructor:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
Apakah ini melayani tujuan penting? Apakah saya tetap bisa mengabaikannya?
javascript
oop
inheritance
trinth
sumber
sumber
subclass.prototype.constructor
akan menunjukparent_class
jika Anda tidak menulissubclass.prototype.constructor = subclass
; Artinya, menggunakansubclass.prototype.constructor()
secara langsung akan menghasilkan hasil yang tidak terduga.Jawaban:
Ini tidak selalu diperlukan, tetapi memang ada kegunaannya. Misalkan kita ingin membuat metode salin pada
Person
kelas dasar . Seperti ini:Sekarang apa yang terjadi ketika kita membuat yang baru
Student
dan menyalinnya?Salinan bukan turunan dari
Student
. Ini karena (tanpa pemeriksaan eksplisit), kami tidak memiliki cara untuk mengembalikanStudent
salinan dari kelas "basis". Kami hanya dapat mengembalikan aPerson
. Namun, jika kami telah mengatur ulang konstruktor:... maka semuanya berfungsi seperti yang diharapkan:
sumber
constructor
Atribut tidak memiliki arti khusus dalam JS, jadi Anda bisa menyebutnyabananashake
. Satu-satunya perbedaan adalah bahwa mesin otomatis menginisialisasiconstructor
padaf.prototype
setiap kali Anda menyatakan fungsif
. Namun, itu dapat ditimpa kapan saja.constructor
berarti bahwa memang memiliki makna khusus dalam JS, cukup banyak menurut definisi.return new this.constructor(this.name);
bukannyareturn new Person(this.name);
. Sejakthis.constructor
adalahStudent
fungsi (karena Anda mengaturnya denganStudent.prototype.constructor = Student;
), yangcopy
fungsi berakhir memanggilStudent
fungsi. Saya tidak yakin apa maksud Anda dengan//just as bad
komentar tersebut.Student
konstruktor telah menambahkan sebuah argumen tambahan seperti:Student(name, id)
? Apakah kita kemudian harus mengesampingkancopy
fungsi, memanggilPerson
versi dari dalamnya, dan kemudian juga menyalinid
properti tambahan ?Iya dan tidak.
Di ES5 dan sebelumnya, JavaScript sendiri tidak digunakan
constructor
untuk apa pun. Itu didefinisikan bahwa objek default pada properti fungsiprototype
akan memilikinya dan itu akan merujuk kembali ke fungsi, dan hanya itu . Tidak ada hal lain dalam spesifikasi yang dimaksud sama sekali.Itu berubah dalam ES2015 (ES6), yang mulai menggunakannya dalam kaitannya dengan hierarki warisan. Misalnya,
Promise#then
menggunakanconstructor
properti dari janji yang Anda panggil (melalui SpeciesConstructor ) ketika membangun janji baru untuk kembali. Ini juga terlibat dalam array subtyping (via ArraySpeciesCreate ).Di luar bahasa itu sendiri, kadang-kadang orang akan menggunakannya ketika mencoba membangun fungsi "klon" generik atau hanya secara umum ketika mereka ingin merujuk pada apa yang mereka yakini sebagai fungsi konstruktor objek. Pengalaman saya adalah bahwa menggunakannya jarang, tetapi kadang-kadang orang menggunakannya.
Itu ada di sana secara default, Anda hanya perlu mengembalikannya ketika Anda mengganti objek pada properti fungsi
prototype
:Jika Anda tidak melakukan ini:
... lalu
Student.prototype.constructor
mewarisi dariPerson.prototype
yang (mungkin) milikiconstructor = Person
. Jadi itu menyesatkan. Dan tentu saja, jika Anda mensubclassing sesuatu yang menggunakannya (sukaPromise
atauArray
) dan tidak menggunakanclass
¹ (yang menangani ini untuk Anda), Anda harus memastikan bahwa Anda menyetelnya dengan benar. Jadi pada dasarnya: Ini ide yang bagus.Tidak apa-apa jika tidak ada dalam kode Anda (atau kode perpustakaan yang Anda gunakan) menggunakannya. Saya selalu memastikan itu terhubung dengan benar.
Tentu saja, dengan
class
kata kunci ES2015 (alias ES6) , sebagian besar waktu kita akan menggunakannya, kita tidak perlu lagi, karena itu ditangani untuk kita ketika kita melakukannya¹ "... jika kamu mensubclassingkan sesuatu yang menggunakannya (suka
Promise
atau tidakArray
) dan tidak menggunakanclass
..." - Itu mungkin untuk melakukan itu, tapi itu sangat menyakitkan (dan agak konyol). Anda harus menggunakanReflect.construct
.sumber
TLDR; Tidak super diperlukan, tetapi mungkin akan membantu dalam jangka panjang, dan lebih akurat untuk melakukannya.
CATATAN: Banyak yang diedit karena jawaban saya sebelumnya ditulis secara membingungkan dan memiliki beberapa kesalahan yang saya lewatkan saat terburu-buru untuk menjawab. Terima kasih kepada mereka yang menunjukkan beberapa kesalahan mengerikan.
Pada dasarnya, ini untuk mengirim subclass dengan benar di Javascript. Ketika kita subkelas, kita harus melakukan beberapa hal funky untuk memastikan bahwa delegasi prototipe berfungsi dengan benar, termasuk menimpa
prototype
objek. Menimpaprototype
objek termasukconstructor
, jadi kita perlu memperbaiki referensi.Mari kita cepat melihat bagaimana 'kelas' dalam ES5 bekerja.
Katakanlah Anda memiliki fungsi konstruktor dan prototipe:
Ketika Anda memanggil konstruktor untuk instantiate, katakan
Adam
:Kata
new
kunci yang dipanggil dengan 'Orang' pada dasarnya akan menjalankan konstruktor Orang dengan beberapa baris kode tambahan:Jika kita
console.log(adam.species)
, lookup akan gagal diadam
contoh, dan mencari rantai prototypal untuk nya.prototype
, yangPerson.prototype
- danPerson.prototype
memiliki sebuah.species
properti, sehingga pencarian akan berhasil diPerson.prototype
. Ini kemudian akan masuk'human'
.Di sini, surat
Person.prototype.constructor
wasiat akan menunjuk dengan benarPerson
.Jadi sekarang bagian yang menarik, yang disebut 'subclassing'. Jika kita ingin membuat
Student
kelas, yaitu subkelas dariPerson
kelas dengan beberapa perubahan tambahan, kita perlu memastikan bahwaStudent.prototype.constructor
poin ke Siswa untuk akurasi.Itu tidak melakukan ini dengan sendirinya. Saat Anda subkelas, kode ini terlihat seperti ini:
Memanggil di
new Student()
sini akan mengembalikan objek dengan semua properti yang kita inginkan. Di sini, jika kita periksaeve instanceof Person
, itu akan kembalifalse
. Jika kami mencoba mengakseseve.species
, itu akan kembaliundefined
.Dengan kata lain, kita perlu mengirimkan delegasi sehingga
eve instanceof Person
mengembalikan true dan agar instanceStudent
delegasi benarStudent.prototype
, dan kemudianPerson.prototype
.TETAPI karena kami menyebutnya dengan
new
kata kunci, ingat apa yang ditambahkan oleh doa itu? Itu akan memanggilObject.create(Student.prototype)
, yang bagaimana kita mengatur hubungan delegasi antaraStudent
danStudent.prototype
. Perhatikan bahwa saat ini,Student.prototype
kosong. Jadi mencari.species
contohStudent
akan gagal karena delegasi untuk hanyaStudent.prototype
, dan.species
properti tidak ada diStudent.prototype
.Ketika kita melakukan assign
Student.prototype
untukObject.create(Person.prototype)
,Student.prototype
dirinya kemudian delegasi untukPerson.prototype
, dan mencarieve.species
akan kembalihuman
seperti yang kita harapkan. Mungkin kita ingin mewarisi dari Student.prototype AND Person.prototype. Jadi kita harus memperbaiki semua itu.Sekarang delegasi berfungsi, tetapi kami menimpa
Student.prototype
dengan dariPerson.prototype
. Jadi jika kita memanggilStudent.prototype.constructor
, itu akan menunjuk kePerson
bukanStudent
. Ini sebabnya kami harus memperbaikinya.Di ES5,
constructor
properti kami adalah referensi yang merujuk ke fungsi yang telah kami tulis dengan maksud untuk menjadi 'konstruktor'. Selain dari apanew
diberikan kata kunci kepada kami, konstruktor adalah fungsi 'polos'.Dalam ES6,
constructor
sekarang dibangun ke cara kita menulis kelas - seperti pada, itu disediakan sebagai metode ketika kita mendeklarasikan kelas. Ini hanyalah gula sintaksis tetapi itu memberi kita beberapa kenyamanan seperti akses kesuper
ketika kita memperluas kelas yang ada. Jadi kita akan menulis kode di atas seperti ini:sumber
eve instanceof Student
dikembalikantrue
. Lihat stackoverflow.com/questions/35537995/… untuk penjelasan. Juga ketika Anda mengatakanwhich is, at the moment, nothing
apa yang Anda maksud? Setiap fungsi memiliki prototipe jadi jika saya periksaStudent.prototype
itu adalah sesuatu.Object.create(Person.prototype)
,Student.prototype
kosong. Jadi jika kita masukeve.species
, ia tidak akan mendelegasikan dengan benar hingga superclass-nya, Person, dan itu tidak akan masuk'human'
. Agaknya, kami ingin setiap subclass mewarisi dari prototipe dan juga prototipe supernya.which is, at the moment, nothing
maksud saya,Student.prototype
benda itu kosong.Student.prototype
keObject.create(Person.prototype)
- yang, jika Anda ingat, cara yang sama semua contoh Person diatur untuk mendelegasikan kePerson.prototype
- mencari properti pada contohStudent
akan didelegasikan hanya untukStudent.prototype
. Jadieve.species
akan gagal pencariannya. Jika kita menugaskannya,Student.prototype
itu sendiri yang kemudian didelegasikanPerson.prototype
, dan mencarieve.species
akan kembalihuman
.instance
konstruktor 'subkelas', itu akan akurat." Tidak,instanceof
tidak digunakanconstructor
. "Namun, jika kita mencari .prototype.constructor siswa, itu masih akan menunjuk ke Person" Tidak, itu akan menjadiStudent
. Saya tidak mengerti maksud dari contoh ini. Memanggil fungsi dalam konstruktor bukanlah warisan. "Dalam ES6, konstruktor sekarang merupakan fungsi aktual dan bukan referensi ke fungsi" Uh apa?Saya tidak setuju. Tidak perlu mengatur prototipe. Ambil kode yang sama persis tetapi hapus garis prototype.constructor. Apakah ada yang berubah? Tidak. Sekarang, buat perubahan berikut:
dan di akhir kode uji ...
Warnanya akan biru.
Perubahan pada prototype.constructor, menurut pengalaman saya, tidak melakukan banyak hal kecuali Anda melakukan hal-hal yang sangat spesifik, sangat rumit yang mungkin bukan praktik yang baik :)
Sunting: Setelah menyodok sekitar web sebentar dan melakukan beberapa eksperimen, sepertinya orang mengatur konstruktor sehingga 'terlihat' seperti hal yang sedang dibangun dengan 'baru'. Saya kira saya berpendapat bahwa masalah dengan ini adalah bahwa javascript adalah bahasa prototipe - tidak ada yang namanya pewarisan. Tetapi kebanyakan programmer berasal dari latar belakang pemrograman yang mendorong pewarisan sebagai 'jalan'. Jadi kami datang dengan segala macam hal untuk mencoba dan membuat bahasa prototipikal ini menjadi bahasa 'klasik' .. seperti memperluas 'kelas'. Sungguh, dalam contoh yang mereka berikan, siswa baru adalah seseorang - itu bukan 'perpanjangan' dari siswa lain .. siswa itu semua tentang orang itu, dan apa pun orang itu, siswa itu juga. Perpanjang siswa, dan apa pun yang Anda
Crockford agak gila dan terlalu bersemangat, tetapi lakukan beberapa bacaan serius pada beberapa hal yang ditulisnya .. itu akan membuat Anda melihat hal ini dengan sangat berbeda.
sumber
Ini memiliki jebakan besar jika Anda menulis
tetapi kemudian jika ada seorang Guru yang prototipenya juga Orang dan Anda menulis
maka konstruktor Siswa sekarang adalah Guru!
Sunting: Anda dapat menghindari ini dengan memastikan bahwa Anda telah menetapkan prototipe Siswa dan Guru menggunakan instance baru dari kelas Person yang dibuat menggunakan Object.create, seperti pada contoh Mozilla.
sumber
Student.prototype = Object.create(...)
diasumsikan dalam pertanyaan ini. Jawaban ini tidak menambah apa pun kecuali kemungkinan kebingungan.Object.create(...)
digunakan dalam artikel MDN yang menelurkan pertanyaan, tetapi tidak dalam pertanyaan itu sendiri. Saya yakin banyak orang tidak mengklik.Sejauh ini kebingungan masih ada.
Mengikuti contoh asli, karena Anda memiliki objek yang ada
student1
sebagai:Misalkan Anda tidak ingin tahu cara
student1
dibuat, Anda hanya ingin objek lain seperti itu, Anda dapat menggunakan properti konstruktorstudent1
seperti:Di sini akan gagal mendapatkan properti dari
Student
jika properti konstruktor tidak disetel. Sebaliknya itu akan membuatPerson
objek.sumber
Punya contoh kode yang bagus tentang mengapa benar-benar perlu untuk mengatur konstruktor prototipe ..
sumber
createNewCar
metode menciptakan pabrik !? Juga sepertinya ini seharusnya digunakanvar audiFactory = new CarFactory("Audi")
daripada menggunakan warisan.this.constructor
internal, jadi tidak mengherankan jika harus ditetapkan. Apakah Anda punya contoh tanpa itu?Tidak perlu untuk fungsi bergula 'kelas' atau menggunakan 'Baru' hari ini. Gunakan objek literal.
Prototipe Obyek sudah menjadi 'kelas'. Ketika Anda mendefinisikan objek literal, itu sudah merupakan contoh dari objek prototipe. Ini juga dapat bertindak sebagai prototipe objek lain, dll.
Ini layak dibaca :
sumber
Hal ini diperlukan ketika Anda membutuhkan alternatif
toString
tanpa monkeypatching:sumber
foo.constructor()
??EDIT, saya sebenarnya salah. Mengomentari batas tidak mengubah perilaku sama sekali. (Saya mengujinya)
Ya itu perlu. Saat kamu melakukan
Student.prototype.constructor
menjadiPerson
. Oleh karena itu, panggilanStudent()
akan mengembalikan objek yang dibuat olehPerson
. Jika Anda melakukannyaStudent.prototype.constructor
diatur ulang keStudent
. Sekarang ketika Anda menyebutnyaStudent()
dijalankanStudent
, yang memanggil konstruktor indukParent()
, ia mengembalikan objek yang diwarisi dengan benar. Jika Anda tidak menyetel ulangStudent.prototype.constructor
sebelum memanggilnya, Anda akan mendapatkan objek yang tidak memiliki properti yang disetelStudent()
.sumber
Diberikan fungsi konstruktor sederhana:
Secara default (dari spesifikasi https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor ), semua prototipe secara otomatis mendapatkan properti bernama konstruktor yang menunjuk kembali ke fungsi. yang merupakan properti. Bergantung pada konstruktornya, properti dan metode lain mungkin ditambahkan ke prototipe yang bukan praktik yang sangat umum tetapi masih diizinkan untuk ekstensi.
Jadi cukup menjawab: kita perlu memastikan bahwa nilai dalam prototype.constructor diatur dengan benar seperti yang seharusnya oleh spesifikasi.
Apakah kita harus selalu menetapkan nilai ini dengan benar? Ini membantu dengan debugging dan membuat struktur internal konsisten terhadap spesifikasi. Kita harus benar-benar tahu kapan API kita digunakan oleh pihak ketiga, tetapi tidak benar-benar ketika kode akhirnya dieksekusi di runtime.
sumber
Inilah salah satu contoh dari MDN yang menurut saya sangat membantu untuk memahami penggunaannya.
Dalam JavaScript, kami memiliki
async functions
yang mengembalikan objek AsyncFunction .AsyncFunction
bukan objek global tetapi orang dapat mengambilnya dengan menggunakanconstructor
properti dan menggunakannya.sumber
Itu tidak perlu. Ini hanyalah salah satu dari banyak hal tradisional, yang dilakukan oleh para juara OOP untuk mencoba mengubah warisan prototipikal JavaScript menjadi warisan klasik. Satu-satunya hal yang mengikuti
tidak, adalah bahwa Anda sekarang memiliki referensi "konstruktor" saat ini.
Dalam jawaban Wayne, yang telah ditandai sebagai benar, Anda dapat melakukan hal yang persis sama dengan kode berikut
dengan kode di bawah ini (ganti saja this.constructor dengan Person)
Terima kasih Tuhan bahwa dengan purist puritan ES6 klasik dapat menggunakan operator asli bahasa seperti kelas, meluas dan super dan kita tidak harus melihat seperti koreksi prototipe. Konstruktor dan referensi orang tua.
sumber