Dalam erti kata sebenar, Javascript bukan bahasa berorientasikan objek dan tidak menyediakan kaedah pewarisan tradisional Walau bagaimanapun, ia menyediakan kaedah pewarisan prototaip, menggunakan sifat prototaip yang disediakan dengan sendirinya untuk mencapai pewarisan.
Prototaip dan Rantaian Prototaip
Sebelum kita bercakap tentang warisan prototaip, kita masih perlu bercakap tentang prototaip dan rantai prototaip Lagipun, ini adalah asas untuk merealisasikan warisan prototaip.
Dalam Javascript, setiap fungsi mempunyai prototaip atribut prototaip yang menunjuk kepada prototaipnya sendiri, dan objek yang dicipta oleh fungsi ini juga mempunyai atribut __proto__ yang menunjuk kepada prototaip ini, dan prototaip fungsi itu ialah objek, jadi objek ini juga akan mempunyai __proto__ menunjuk ke prototaipnya sendiri, dan pergi lebih dalam lapisan demi lapisan sehingga ia mencapai prototaip objek Objek, dengan itu membentuk rantai prototaip. Gambar di bawah menerangkan hubungan antara prototaip dan rantai prototaip dalam Javascript dengan baik.
Setiap fungsi ialah objek yang dicipta oleh fungsi Fungsi, jadi setiap fungsi juga mempunyai atribut __proto__ yang menunjuk kepada prototaip fungsi Fungsi. Apa yang perlu dinyatakan di sini ialah atribut __proto__ bagi setiap objek yang sebenarnya membentuk rantai prototaip, bukan atribut prototaip fungsi, yang sangat penting.
Warisan prototaip
Mod Asas
var Child = function(){
This.name = 'anak' ;
} ;
Child.prototype = new Parent() ;
var parent = new Parent() ;
var child = new Child() ;
console.log(parent.getName()) ; //parent
console.log(child.getName()); //child
Ini ialah cara paling mudah untuk melaksanakan warisan prototaip secara langsung menetapkan objek kelas induk kepada prototaip pembina subkelas, supaya objek subkelas boleh mengakses atribut dalam prototaip kelas induk dan pembina kelas induk. Gambar rajah pewarisan prototaip kaedah ini adalah seperti berikut:
Kelebihan kaedah ini adalah jelas Pelaksanaannya sangat mudah dan tidak memerlukan sebarang operasi khas Pada masa yang sama, kelemahannya juga jelas jika subkelas perlu melakukan tindakan permulaan yang sama seperti dalam kelas induk pembina, maka ia mesti dilakukan dalam subkelas Dalam pembina, ulangi operasi dalam kelas induk:
var Anak = fungsi(nama){
This.name = nama ||. 'anak' ;
} ;
Child.prototype = new Parent() ;
var parent = new Parent('myParent') ;
var child = new Child('myChild') ;
console.log(parent.getName()) ; //myParent
console.log(child.getName()); //myChild
Dalam kes di atas, hanya atribut nama yang perlu dimulakan Jika kerja pemulaan terus meningkat, kaedah ini sangat menyusahkan. Oleh itu, terdapat kaedah penambahbaikan berikut.
pinjam pembina
var Anak = fungsi(nama){
Parent.apply(this,arguments) ;
} ;
Child.prototype = new Parent() ;
var parent = new Parent('myParent') ;
var child = new Child('myChild') ;
console.log(parent.getName()) ; //myParent
console.log(child.getName()); //myChild
Kaedah di atas melakukan kerja pemulaan yang sama dengan memanggil pembina kelas induk melalui aplikasi dalam pembina subkelas Dengan cara ini, tidak kira berapa banyak kerja pemulaan dilakukan dalam kelas induk, subkelas juga boleh melakukan perkara yang sama kerja permulaan. Tetapi masih terdapat masalah dengan pelaksanaan di atas Pembina kelas induk dilaksanakan dua kali, sekali dalam pembina subkelas dan sekali apabila menetapkan prototaip subkelas Ini sangat berlebihan, jadi kita masih perlu membuat penambahbaikan
Parent.apply(this,arguments) ;
} ;
Child.prototype = Induk.prototaip ;
var child = new Child('myChild') ;
console.log(child.getName()); //myChild
Corak Pembina Sementara (Corak Cawan Suci)
Versi penambahbaikan terakhir yang meminjam corak pembina di atas masih mempunyai masalah Ia memberikan prototaip kelas induk secara langsung kepada prototaip subkelas Ini akan menyebabkan masalah, iaitu, jika prototaip subkelas diubah suai , maka Pengubahsuaian ini juga akan mempengaruhi prototaip kelas induk, dan dengan itu objek kelas induk Ini pastinya bukan perkara yang semua orang mahu lihat. Untuk menyelesaikan masalah ini, terdapat corak pembina sementara.
Parent.apply(this,arguments) ;
} ;
var F = Fungsi baharu(){};
F.prototaip = Induk.prototaip ;
Child.prototype = F() baharu ;
var child = new Child('myChild') ;
console.log(child.getName()); //myChild
Adalah mudah untuk melihat bahawa dengan menambahkan pembina sementara F antara prototaip kelas induk dan prototaip subkelas, sambungan antara prototaip subkelas dan prototaip kelas induk terputus, supaya apabila prototaip subkelas diubah suai Ia akan tidak menjejaskan prototaip kelas induk.
Kaedah saya
Mod Holy Grail telah tamat dalam "Mod Javascript", tetapi tidak kira kaedah di atas, terdapat masalah yang tidak mudah ditemui. Anda boleh melihat bahawa saya menambah atribut literal objek obj pada atribut prototaip 'Ibu bapa', tetapi ia tidak pernah digunakan. Mari kita lihat situasi berikut berdasarkan model Holy Grail:
Parent.apply(this,arguments) ;
} ;
var F = Fungsi baharu(){};
F.prototaip = Induk.prototaip ;
Child.prototype = F() baharu ;
var child = new Child('myChild') ;
console.log(parent.obj.a); //1
child.obj.a = 2;
console.log(child.obj.a); //2
console.log(parent.obj.a); //2
Dalam situasi di atas, apabila saya mengubah suai objek anak obj.a, obj.a dalam prototaip kelas induk juga akan diubah suai, yang menyebabkan masalah yang sama seperti prototaip yang dikongsi. Keadaan ini berlaku kerana apabila mengakses child.obj.a, kami akan mengikuti rantaian prototaip untuk mencari prototaip kelas induk, kemudian mencari atribut obj, dan kemudian mengubah suai obj.a. Mari kita lihat situasi berikut:
var Anak = fungsi(nama){
Parent.apply(this,arguments) ;
} ;
var F = Fungsi baharu(){};
F.prototaip = Induk.prototaip ;
Child.prototype = F() baharu ;
var parent = new Parent('myParent') ;
var child = new Child('myChild') ;
console.log(child.obj.a) ; //1
console.log(parent.obj.a); //1
child.obj.a = 2;
console.log(child.obj.a); //2
console.log(parent.obj.a); //2
Terdapat isu utama di sini Apabila objek mengakses sifat dalam prototaip, sifat dalam prototaip adalah baca sahaja untuk objek, maksudnya, objek kanak-kanak boleh membaca objek obj, tetapi tidak boleh mengubah suai objek obj dalam prototaip, jadi apabila kanak-kanak mengubah suai obj, ia tidak akan menjejaskan obj dalam prototaip Ia hanya menambah atribut obj pada objeknya sendiri, mengatasi atribut obj dalam prototaip kelas induk. Apabila objek kanak-kanak mengubah suai obj.a, ia mula-mula membaca rujukan kepada obj dalam prototaip Pada masa ini, child.obj dan Parent.prototype.obj menghala ke objek yang sama, jadi pengubahsuaian obj.a oleh kanak-kanak itu akan. mempengaruhi Nilai Parent.prototype.obj.a, yang seterusnya mempengaruhi objek kelas induk. Kaedah pewarisan $scope nesting dalam AngularJS dilaksanakan oleh pewarisan prototaip dalam Javascript.
Menurut penerangan di atas, selagi prototaip yang diakses dalam objek subkelas adalah objek yang sama dengan prototaip kelas induk, maka keadaan di atas akan berlaku, jadi kita boleh menyalin prototaip kelas induk dan kemudian menetapkannya kepada prototaip subkelas, jadi Apabila subkelas mengubah suai sifat dalam prototaip, ia hanya mengubah suai salinan prototaip kelas induk dan tidak menjejaskan prototaip kelas induk. Pelaksanaan khusus adalah seperti berikut:
Parent.apply(this,arguments) ;
} ;
Child.prototype = deepClone(Parent.prototype) ;
var parent = new Parent('parent') ;
console.log(parent.obj.a); //1
child.obj.a = '2' ;
console.log(child.obj.a); //2
console.log(parent.obj.a); //1
var extend = fungsi(Ibu bapa, Anak){
Kanak-kanak = Kanak-kanak || function(){} ;
jika(Ibu bapa === tidak ditentukan)
kembali Anak ;
//借用父类构造函数
Kanak-kanak = fungsi(){
Parent.apply(this,argument) ;
} ;
//通过深拷贝继承父类原型
Child.prototype = deepClone(Parent.prototype) ;
//重置constructor属性
Child.prototype.constructor = Kanak-kanak ;
} ;
总结
说了这么多,其实Javascript中实现继承是十分灵活多样的,并没有一种最好的有最好的间最好的有法求实现不同方式的继承,最重要的是要理解Javascript中实现继承的原理,也就是原型和原型链的问题,只要理解了这些,自己实现继承就可以渥刃。