Rumah >hujung hadapan web >tutorial js >Corak untuk Warisan Objek di JavaScript ES2015

Corak untuk Warisan Objek di JavaScript ES2015

Lisa Kudrow
Lisa Kudrowasal
2025-02-16 11:38:39759semak imbas

Corak untuk Warisan Objek di JavaScript ES2015

Takeaways Key

    Dengan ES2015, JavaScript kini mempunyai sintaks khusus untuk menentukan kelas untuk memastikan kod bersih, meminimumkan kedalaman hierarki, dan elakkan kod pendua.
  • Pewarisan berganda, ciri yang disokong oleh beberapa bahasa OOP klasik, membolehkan penciptaan kelas yang mewarisi dari pelbagai kelas asas. Walau bagaimanapun, ia mengalami masalah berlian, di mana dua kelas induk menentukan kaedah yang sama.
  • Mixins, kelas kecil yang hanya mengandungi kaedah, adalah satu lagi strategi yang digunakan untuk mengelak masalah berlian. Daripada memperluaskan kelas ini, mixin dimasukkan ke dalam kelas lain.
  • Walaupun sintaks kelas memberikan ilusi bahawa JavaScript adalah bahasa OOP berasaskan kelas, tidak. Kebanyakan pendekatan memerlukan mengubahsuai prototaip objek untuk meniru pelbagai warisan. Menggunakan fungsi kilang kelas adalah strategi yang boleh diterima untuk menggunakan mixins untuk menyusun kelas.
Corak untuk Warisan Objek di JavaScript ES2015 Dengan ketibaan ES2015 yang lama ditunggu-tunggu (dahulunya dikenali sebagai ES6), JavaScript dilengkapi dengan sintaks khusus untuk menentukan kelas. Dalam artikel ini, saya akan meneroka jika kita boleh memanfaatkan sintaks kelas untuk menyusun kelas daripada bahagian yang lebih kecil.

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>

rekap: sintaks kelas es2015

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:

  • kelas foo {...} menerangkan kelas bernama foo
  • kelas foo memanjangkan bar {...} menerangkan kelas, foo, yang memanjangkan kelas lain, bar

Dalam blok kelas, kita boleh menentukan sifat kelas itu. Untuk artikel ini, kita hanya perlu memahami pembina dan kaedah:

  • pembina () {...} adalah fungsi terpelihara yang dilaksanakan pada penciptaan (baru foo ())
  • foo () {...} mencipta kaedah bernama foo

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.

Mengarang kelas ES2015

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.

object.assign (childlass.prototype, mixin ...)

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

  • Mixins tidak boleh dimulakan

cons

  • memerlukan baris tambahan kod
  • object.assign () adalah sedikit kabur
  • mencipta semula warisan prototaip untuk bekerja dengan kelas ES2015

Mengarang objek dalam pembina

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

  • ia berfungsi, saya rasa?

cons

  • sangat kabur
  • Manfaat Zero dari sintaks kelas ES2015
  • penyalahgunaan kelas ES2015

Fungsi kilang kelas

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

  • lebih mudah difahami, kerana semua maklumat ada dalam pengepala pengisytiharan kelas

cons

  • Membuat kelas pada runtime mungkin memberi kesan kepada prestasi permulaan dan/atau penggunaan memori

Kesimpulan

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 perbezaan antara warisan klasik dan prototypal dalam JavaScript? Kelas mentakrifkan pelan tindakan untuk objek dan objek adalah contoh kelas. Warisan dicapai dengan membuat subclass dari superclass. Sebaliknya, JavaScript menggunakan warisan prototip di mana objek mewarisi secara langsung dari objek lain. Ini lebih fleksibel kerana objek boleh dilanjutkan atau diubah secara dinamik pada runtime.

Bagaimana kata kunci 'super' berfungsi dalam javascript es2015? fungsi panggilan pada ibu bapa objek. Apabila digunakan dalam pembina, kata kunci 'super' muncul bersendirian dan mesti digunakan sebelum kata kunci 'ini' digunakan. Kata kunci 'super' juga boleh digunakan untuk memanggil fungsi pada objek induk dalam kaedah.

Apakah 'prototaip' dalam JavaScript dan bagaimana ia digunakan dalam warisan? Harta ini adalah rujukan kepada objek lain, objek prototaip. Apabila fungsi dibuat, objek prototaipnya juga dibuat dan dihubungkan melalui sifat prototaip fungsi. Apabila objek dibuat menggunakan fungsi pembina, ia mewarisi sifat dan kaedah dari prototaip pembina. Walau bagaimanapun, ia boleh dicapai secara tidak langsung menggunakan mixins. Mixin adalah teknik yang melibatkan penyalinan sifat dari satu objek ke yang lain. Ini membolehkan objek mewarisi sifat dan kaedah dari pelbagai sumber.

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. >

Dalam JavaScript, anda boleh menambah harta kepada prototaip objek dengan hanya memberikan nilai kepada harta pada objek prototaip. Harta ini kemudiannya akan diwarisi oleh semua objek yang dibuat dari fungsi pembina.

Atas ialah kandungan terperinci Corak untuk Warisan Objek di JavaScript ES2015. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
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