Rumah >hujung hadapan web >tutorial js >Corak untuk Warisan Objek di JavaScript ES2015
Menjaga kedalaman hierarki sekurang -kurangnya adalah penting untuk memastikan kod anda bersih. Menjadi pintar tentang bagaimana anda berpecah kelas membantu. Untuk asas kod besar, satu pilihan adalah untuk membuat kelas daripada bahagian yang lebih kecil; Menyusun kelas. Ia juga merupakan strategi yang sama untuk mengelakkan kod pendua.
Bayangkan kita sedang membina permainan di mana pemain tinggal di dunia haiwan. Ada yang berteman, yang lain bermusuhan (orang anjing seperti saya mungkin mengatakan semua kucing adalah makhluk yang bermusuhan). Kita boleh membuat kelas Hostleanimal, yang memanjangkan haiwan, untuk berfungsi sebagai kelas asas untuk kucing. Pada satu ketika, kami memutuskan untuk menambah robot yang direka untuk membahayakan manusia. Perkara pertama yang kita lakukan ialah membuat kelas robot. Kami kini mempunyai dua kelas yang mempunyai sifat yang sama. Kedua -dua Hostileanimal dan Robot dapat menyerang (), misalnya.
Jika kita boleh menentukan permusuhan dalam kelas atau objek yang berasingan, katakanlah bermusuhan, kita boleh menggunakannya semula untuk kedua -dua kucing sebagai robot. Kita boleh melakukannya dengan pelbagai cara.
Pewarisan berganda adalah ciri beberapa sokongan bahasa OOP klasik. Seperti namanya, ia memberi kita keupayaan membuat kelas yang mewarisi dari pelbagai kelas asas. Lihat bagaimana kelas kucing memanjangkan pelbagai kelas asas dalam kod python berikut:
<span>class Animal(object): </span> <span>def walk(self): </span> <span># ... </span> <span>class Hostile(object): </span> <span>def attack(self, target): </span> <span># ... </span> <span>class Dog(Animal): </span> <span># ... </span> <span>class Cat(Animal, Hostile): </span> <span># ... </span> dave <span>= Cat(); </span>dave<span>.walk(); </span>dave<span>.attack(target); </span>Antara muka
adalah ciri umum dalam bahasa OOP klasik (ditaip). Ia membolehkan kita menentukan kaedah (dan kadang -kadang sifat) kelas harus mengandungi. Jika kelas itu tidak, pengkompil akan menimbulkan kesilapan. Kod TypeScript berikut akan menimbulkan ralat jika CAT tidak mempunyai serangan () atau berjalan () kaedah:
<span>class Animal(object): </span> <span>def walk(self): </span> <span># ... </span> <span>class Hostile(object): </span> <span>def attack(self, target): </span> <span># ... </span> <span>class Dog(Animal): </span> <span># ... </span> <span>class Cat(Animal, Hostile): </span> <span># ... </span> dave <span>= Cat(); </span>dave<span>.walk(); </span>dave<span>.attack(target); </span>
Pewarisan berganda mengalami masalah berlian (di mana dua kelas induk menentukan kaedah yang sama). Sesetengah bahasa mengelak masalah ini dengan melaksanakan strategi lain, seperti Mixins . Mixins adalah kelas kecil yang hanya mengandungi kaedah. Daripada memperluaskan kelas ini, mixin dimasukkan ke dalam kelas lain. Dalam PHP, sebagai contoh, mixin dilaksanakan menggunakan ciri -ciri.
<span>interface Hostile { </span> <span>attack(); </span><span>} </span> <span>class Animal { </span> <span>walk(); </span><span>} </span> <span>class Dog extends Animal { </span> <span>// ... </span><span>} </span> <span>class Cat extends Animal implements Hostile { </span> <span>attack() { </span> <span>// ... </span> <span>} </span><span>} </span>
Jika anda tidak mempunyai peluang untuk menyelam kelas ES2015 atau merasa anda tidak tahu cukup tentang mereka, pastikan anda membaca JavaScript yang berorientasikan objek Jeff Mott-menyelam dalam kelas ES6 sebelum anda meneruskan.
secara ringkas:
Dalam blok kelas, kita boleh menentukan sifat kelas itu. Untuk artikel ini, kita hanya perlu memahami pembina dan kaedah:
Sintaks kelas kebanyakannya gula sintaksis berbanding model prototaip JavaScript. Daripada membuat kelas, ia mewujudkan pembina fungsi:
<span>class Animal { </span> <span>// ... </span><span>} </span> <span>trait Hostile { </span> <span>// ... </span><span>} </span> <span>class Dog extends Animal { </span> <span>// ... </span><span>} </span> <span>class Cat extends Animal { </span> <span>use Hostile; </span> <span>// ... </span><span>} </span> <span>class Robot { </span> <span>use Hostile; </span> <span>// ... </span><span>} </span>
Takeaway di sini adalah bahawa JavaScript bukan bahasa berasaskan kelas, OOP. Seseorang mungkin berpendapat bahawa sintaks itu menipu, memberikan gambaran bahawa ia adalah.
antara muka boleh ditiru dengan membuat kaedah dummy yang melemparkan ralat. Sebaik sahaja diwarisi, fungsi mesti ditindih untuk mengelakkan kesilapan:
<span>class Foo {} </span><span>console.log(typeof Foo); // "function" </span>
Seperti yang dicadangkan sebelum ini, pendekatan ini bergantung kepada warisan. Untuk mewarisi pelbagai kelas, kita akan memerlukan pelbagai warisan atau campuran.
Pendekatan lain adalah untuk menulis fungsi utiliti yang mengesahkan kelas selepas ia ditakrifkan. Contohnya boleh didapati dalam Wait A Moment, JavaScript menyokong pelbagai warisan! oleh Andrea Giammarchi. Lihat bahagian "Pemeriksaan fungsi objek."
Masa untuk meneroka pelbagai cara untuk memohon pelbagai warisan dan campuran. Semua strategi yang diperiksa di bawah boleh didapati di GitHub.
pra-es2015, kami menggunakan prototaip untuk warisan. Semua fungsi mempunyai harta prototaip. Apabila membuat contoh menggunakan MyFunction baru (), prototaip disalin ke harta dalam contoh. Apabila anda cuba mengakses harta yang tidak ada dalam keadaan, enjin JavaScript akan cuba mencarinya dalam objek prototaip.
untuk menunjukkan, lihat kod berikut:
<span>class IAnimal { </span> <span>walk() { </span> <span>throw new Error('Not implemented'); </span> <span>} </span><span>} </span> <span>class Dog extends IAnimal { </span> <span>// ... </span><span>} </span> <span>const robbie = new Dog(); </span>robbie<span>.walk(); // Throws an error </span>
Objek prototaip ini boleh dibuat dan diubahsuai semasa runtime. Pada mulanya, saya cuba menggunakan kelas untuk haiwan dan bermusuhan:
<span>class Animal(object): </span> <span>def walk(self): </span> <span># ... </span> <span>class Hostile(object): </span> <span>def attack(self, target): </span> <span># ... </span> <span>class Dog(Animal): </span> <span># ... </span> <span>class Cat(Animal, Hostile): </span> <span># ... </span> dave <span>= Cat(); </span>dave<span>.walk(); </span>dave<span>.attack(target); </span>
di atas tidak berfungsi kerana kaedah kelas adalah tidak boleh dihitung . Secara praktiknya, ini bermakna object.assign (...) tidak menyalin kaedah dari kelas. Ini juga menjadikannya sukar untuk mewujudkan fungsi yang menyalin kaedah dari satu kelas ke kelas yang lain. Walau bagaimanapun, kita boleh menyalin setiap kaedah secara manual:
<span>interface Hostile { </span> <span>attack(); </span><span>} </span> <span>class Animal { </span> <span>walk(); </span><span>} </span> <span>class Dog extends Animal { </span> <span>// ... </span><span>} </span> <span>class Cat extends Animal implements Hostile { </span> <span>attack() { </span> <span>// ... </span> <span>} </span><span>} </span>
cara lain adalah untuk kelas parit dan menggunakan objek sebagai mixins. Kesan sampingan yang positif ialah objek Mixin tidak dapat digunakan untuk membuat contoh, mencegah penyalahgunaan.
<span>class Animal { </span> <span>// ... </span><span>} </span> <span>trait Hostile { </span> <span>// ... </span><span>} </span> <span>class Dog extends Animal { </span> <span>// ... </span><span>} </span> <span>class Cat extends Animal { </span> <span>use Hostile; </span> <span>// ... </span><span>} </span> <span>class Robot { </span> <span>use Hostile; </span> <span>// ... </span><span>} </span>
pro
cons
dengan kelas ES2015, anda boleh mengatasi contoh dengan mengembalikan objek dalam pembina:
<span>class Foo {} </span><span>console.log(typeof Foo); // "function" </span>
kita boleh memanfaatkan ciri itu untuk menyusun objek dari pelbagai kelas di dalam subkelas. Perhatikan bahawa object.assign (...) masih tidak berfungsi dengan baik dengan kelas mixin, jadi saya menggunakan objek di sini juga:
<span>class IAnimal { </span> <span>walk() { </span> <span>throw new Error('Not implemented'); </span> <span>} </span><span>} </span> <span>class Dog extends IAnimal { </span> <span>// ... </span><span>} </span> <span>const robbie = new Dog(); </span>robbie<span>.walk(); // Throws an error </span>
Oleh kerana ini merujuk kepada kelas (dengan kaedah yang tidak dapat dipertahankan) dalam konteks di atas, object.assign (..., ini) tidak menyalin kaedah kucing. Sebaliknya, anda perlu menetapkan medan dan kaedah ini secara eksplisit agar objek.assign () dapat menerapkannya, seperti:
<span>function <span>MyFunction</span> () { </span> <span>this.myOwnProperty = 1; </span><span>} </span><span>MyFunction.prototype.myProtoProperty = 2; </span> <span>const myInstance = new MyFunction(); </span> <span>// logs "1" </span><span>console.log(myInstance.myOwnProperty); </span><span>// logs "2" </span><span>console.log(myInstance.myProtoProperty); </span> <span>// logs "true", because "myOwnProperty" is a property of "myInstance" </span><span>console.log(myInstance.hasOwnProperty('myOwnProperty')); </span><span>// logs "false", because "myProtoProperty" isn’t a property of "myInstance", but "myInstance.__proto__" </span><span>console.log(myInstance.hasOwnProperty('myProtoProperty')); </span>
Pendekatan ini tidak praktikal. Kerana anda mengembalikan objek baru dan bukannya contoh, ia pada dasarnya bersamaan dengan:
<span>class Animal { </span> <span>walk() { </span> <span>// ... </span> <span>} </span><span>} </span> <span>class Dog { </span> <span>// ... </span><span>} </span> <span>Object.assign(Dog.prototype, Animal.prototype); </span>
Saya fikir kita boleh bersetuju bahawa yang terakhir lebih mudah dibaca.
pro
cons
Pendekatan ini memanfaatkan keupayaan JavaScript untuk menentukan kelas semasa runtime.
Pertama, kita memerlukan kelas asas. Dalam contoh kami, haiwan dan robot berfungsi sebagai kelas asas. Sekiranya anda ingin bermula dari awal, kelas kosong juga berfungsi.
<span>Object.assign(Cat.prototype, { </span> <span>attack: Hostile.prototype.attack, </span> <span>walk: Animal.prototype.walk, </span><span>}); </span>
Seterusnya, kita perlu membuat fungsi kilang yang mengembalikan kelas baru yang memanjangkan pangkalan kelas, yang diluluskan sebagai parameter. Ini adalah Mixins:
<span>const Animal = { </span> <span>walk() { </span> <span>// ... </span> <span>}, </span><span>}; </span> <span>const Hostile = { </span> <span>attack(target) { </span> <span>// ... </span> <span>}, </span><span>}; </span> <span>class Cat { </span> <span>// ... </span><span>} </span> <span>Object.assign(Cat.prototype, Animal, Hostile); </span>
Sekarang kita boleh lulus mana -mana kelas ke fungsi bermusuhan yang akan mengembalikan kelas baru yang menggabungkan bermusuhan dan apa sahaja kelas yang kita lulus kepada fungsi:
<span>class Answer { </span> <span>constructor(question) { </span> <span>return { </span> <span>answer: 42, </span> <span>}; </span> <span>} </span><span>} </span> <span>// { answer: 42 } </span><span>new Answer("Life, the universe, and everything"); </span>
kita boleh paip melalui beberapa kelas untuk memohon pelbagai mixins:
<span>const Animal = { </span> <span>walk() { </span> <span>// ... </span> <span>}, </span><span>}; </span> <span>const Hostile = { </span> <span>attack(target) { </span> <span>// ... </span> <span>}, </span><span>}; </span> <span>class Cat { </span> <span>constructor() { </span> <span>// Cat-specific properties and methods go here </span> <span>// ... </span> <span>return Object.assign( </span> <span>{}, </span> <span>Animal, </span> <span>Hostile, </span> <span>this </span> <span>); </span> <span>} </span><span>} </span>Anda juga boleh menggunakan objek sebagai kelas asas:
<span>class Cat { </span> <span>constructor() { </span> <span>this.purr = () => { </span> <span>// ... </span> <span>}; </span> <span>return Object.assign( </span> <span>{}, </span> <span>Animal, </span> <span>Hostile, </span> <span>this </span> <span>); </span> <span>} </span><span>} </span>
pro
cons
Apabila saya memutuskan untuk menyelidik topik ini dan menulis artikel mengenainya, saya menjangkakan model prototaip JavaScript akan membantu untuk menjana kelas. Kerana sintaks kelas membuat kaedah tidak dapat diperoleh, manipulasi objek menjadi lebih sukar, hampir tidak praktikal.
Sintaks kelas mungkin mencipta ilusi bahawa JavaScript adalah bahasa OOP berasaskan kelas, tetapi tidak. Dengan kebanyakan pendekatan, anda perlu mengubahsuai prototaip objek untuk meniru warisan berganda. Pendekatan terakhir, menggunakan fungsi kilang kelas, adalah strategi yang boleh diterima untuk menggunakan mixins untuk menyusun kelas.
Jika anda mendapati pengaturcaraan berasaskan prototaip yang ketat, anda mungkin mahu melihat minda anda. Prototaip memberikan fleksibiliti yang tiada tandingan yang boleh anda ambil.
Jika, atas sebab apa pun, anda masih lebih suka pengaturcaraan klasik, anda mungkin ingin melihat bahasa yang disusun kepada JavaScript. Typescript, sebagai contoh, adalah superset JavaScript yang menambah (pilihan) menaip statik dan corak yang anda akan kenali dari bahasa OOP klasik yang lain.
Adakah anda akan menggunakan salah satu pendekatan di atas dalam projek anda? Adakah anda mendapat pendekatan yang lebih baik? Beritahu saya dalam komen!
Artikel ini disemak oleh Jeff Mott, Scott Molinari, Vildan Softic, dan Joan Yin. Terima kasih kepada semua pengulas rakan sebaya SitePoint untuk membuat kandungan SitePoint yang terbaik boleh!Soalan Lazim mengenai JavaScript ES2015 Objek Warisan
Apakah 'pembina' dalam JavaScript ES2015 dan bagaimana ia digunakan dalam warisan? Kaedah khas yang digunakan untuk membuat dan memulakan objek dalam kelas. Dalam konteks warisan, pembina subkelas mesti memanggil pembina superclass menggunakan kata kunci 'super' sebelum ia boleh menggunakan kata kunci 'ini'. >
Kata kunci 'Extends' dalam JavaScript ES2015 digunakan untuk membuat subclass dari superclass. Subclass mewarisi semua sifat dan kaedah superclass, tetapi juga boleh menambah yang baru atau mengatasi yang diwarisi. > Dalam JavaScript, 'kelas' adalah jenis fungsi yang digunakan sebagai pelan tindakan untuk membuat objek. Ia merangkumi data dan fungsi yang beroperasi pada data tersebut. Sebaliknya, 'prototaip' adalah objek yang mana objek lain mewarisi sifat dan kaedah. Anda boleh mengatasi kaedah dalam subkelas dengan hanya menentukan kaedah dengan nama yang sama dalam subclass. Kaedah baru akan digunakan dan bukannya yang diwarisi apabila ia dipanggil pada contoh subclass. > Kata kunci 'baru' dalam JavaScript digunakan untuk membuat contoh kelas atau fungsi pembina. Dalam konteks warisan, kata kunci 'baru' digunakan untuk membuat contoh subclass yang mewarisi sifat dan kaedah dari superclass. >Atas ialah kandungan terperinci Corak untuk Warisan Objek di JavaScript ES2015. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!