Mari kita bercakap tentang kemahiran javascript prototaip inheritance_javascript

WBOY
Lepaskan: 2016-05-16 16:31:45
asal
1183 orang telah melayarinya

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

Salin kod Kod adalah seperti berikut:

var Induk = function(){
This.name = 'ibu bapa' ;
} ;
Parent.prototype.getName = function(){
Kembalikan nama ini ;
} ;
Parent.prototype.obj = {a : 1} ;

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:

Salin kod Kod adalah seperti berikut:

var Induk = fungsi(nama){
This.name = nama ||. 'ibu bapa' ;
} ;
Parent.prototype.getName = function(){
Kembalikan nama ini ;
} ;
Parent.prototype.obj = {a : 1} ;

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

Salin kod Kod adalah seperti berikut:

var Induk = fungsi(nama){
This.name = nama ||. 'ibu bapa' ;
} ;
Parent.prototype.getName = function(){
Kembalikan nama ini ;
} ;
Parent.prototype.obj = {a : 1} ;

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

Salin kod Kod adalah seperti berikut:
var Induk = fungsi(nama){
This.name = nama ||. 'ibu bapa' ;
} ;
Parent.prototype.getName = function(){
Kembalikan nama ini ;
} ;
Parent.prototype.obj = {a : 1} ;
var Anak = fungsi(nama){

Parent.apply(this,arguments) ;
} ;
Child.prototype = Induk.prototaip ;

var parent = new Parent('myParent') ;

var child = new Child('myChild') ;

console.log(parent.getName()) ; //myParent

console.log(child.getName()); //myChild

Dengan cara ini, kita hanya perlu melaksanakan pembina kelas induk sekali dalam pembina kelas anak, dan pada masa yang sama, kita boleh mewarisi atribut dalam prototaip kelas induk Ini juga lebih selari dengan niat asal prototaip, iaitu meletakkan kandungan yang perlu digunakan semula dalam Dalam prototaip, kami hanya mewarisi kandungan boleh guna semula dalam prototaip. Prototaip kaedah di atas adalah seperti berikut:

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.

Salin kod Kod adalah seperti berikut:
var Induk = fungsi(nama){
This.name = nama ||. 'ibu bapa' ;
} ;
Parent.prototype.getName = function(){
Kembalikan nama ini ;
} ;
Parent.prototype.obj = {a : 1} ;
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(parent.getName()) ; //myParent

console.log(child.getName()); //myChild

Rajah pewarisan prototaip kaedah ini adalah seperti berikut:

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:

Salin kod Kod adalah seperti berikut:
var Induk = fungsi(nama){
This.name = nama ||. 'ibu bapa' ;
} ;
Parent.prototype.getName = function(){
Kembalikan nama ini ;
} ;
Parent.prototype.obj = {a : 1} ;
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

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:

Salin kod Kod adalah seperti berikut:

var Induk = fungsi(nama){
This.name = nama ||. 'ibu bapa' ;
} ;
Parent.prototype.getName = function(){
Kembalikan nama ini ;
} ;
Parent.prototype.obj = {a : 1} ;

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:

Salin kod Kod adalah seperti berikut:

var deepClone = fungsi(sumber,sasaran){
Sumber = sumber ||. {} ;
var toStr = Object.prototype.toString ,
arrStr = '[tatasusunan objek]' ;
untuk(var i dalam sumber){
If(source.hasOwnProperty(i)){
            var item = sumber[i] ;
Jika(jenis item === 'objek'){
target[i] = (toStr.apply(item).toLowerCase() === arrStr) : [] ? {} ;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                deepClone(item,sasaran[i]);                } lain{
                   deepClone(item,sasaran[i]) ;
            }
}
}
Pulangan sasaran ;
} ;
var Induk = fungsi(nama){
This.name = nama ||. 'ibu bapa' ;
} ;
Parent.prototype.getName = function(){
Kembalikan nama ini ;
} ;
Parent.prototype.obj = {a : '1'} ;
var Anak = fungsi(nama){

Parent.apply(this,arguments) ;
} ;
Child.prototype = deepClone(Parent.prototype) ;

var child = new Child('child') ;

var parent = new Parent('parent') ;

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); //1

Berdasarkan semua pertimbangan di atas, pelaksanaan khusus warisan Javascript adalah seperti berikut. Di sini kami hanya mempertimbangkan kes di mana Anak dan Ibu Bapa adalah kedua-dua fungsi:

Salin kod Kod adalah seperti berikut:

var deepClone = fungsi(sumber,sasaran){
    sumber = sumber || {} ;
    var toStr = Object.prototype.toString ,
        arrStr = '[tatasusunan objek]' ;
    untuk(var i dalam sumber){
        if(source.hasOwnProperty(i)){
            var item = sumber[i] ;
            if(jenis item === 'objek'){
                target[i] = (toStr.apply(item).toLowerCase() === arrStr) : [] ? {} ;
                deepClone(item,sasaran[i]);   
            }lain{
                deepClone(item,sasaran[i]) ;
            }
        }
    }
    kembali sasaran ;
} ;

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中实现继承的原理,也就是原型和原型链的问题,只要理解了这些,自己实现继承就可以渥刃。

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!