JavaScript ialah bahasa berorientasikan objek. Terdapat pepatah yang sangat klasik dalam JavaScript, semuanya adalah objek. Oleh kerana ia berorientasikan objek, ia mempunyai tiga ciri utama berorientasikan objek: enkapsulasi, pewarisan dan polimorfisme. Apa yang saya bincangkan di sini ialah pewarisan JavaScript, dan saya akan bercakap tentang dua yang lain kemudian.
Warisan JavaScript berbeza daripada warisan C adalah berdasarkan kelas, manakala warisan JavaScript adalah berdasarkan prototaip.
Sekarang datang masalah.
Apakah prototaip? Untuk prototaip, kita boleh merujuk kepada kelas dalam C, yang juga menyimpan sifat dan kaedah objek. Contohnya, mari tulis objek ringkas
Kita dapat melihat bahawa ini ialah objek Haiwan, yang mempunyai nama atribut dan setName kaedah. Perlu diingat bahawa sebaik sahaja prototaip diubah suai, seperti menambah kaedah, semua contoh objek akan berkongsi kaedah ini. Contohnya
Pada masa ini, haiwan hanya mempunyai atribut nama. Jika kita menambah ayat,
Pada masa ini, haiwan juga akan mempunyai kaedah setName.
Warisi salinan ini - bermula dari objek kosong Kita tahu bahawa antara jenis asas JS, terdapat satu objek yang dipanggil, dan contoh paling asasnya ialah objek kosong, iaitu, contoh yang dihasilkan dengan memanggil Objek baru secara langsung. (), atau Ia diisytiharkan menggunakan literal { }. Objek kosong ialah "objek bersih" dengan hanya sifat dan kaedah yang dipratentukan, dan semua objek lain mewarisi daripada objek kosong, jadi semua objek mempunyai sifat dan kaedah yang dipratentukan ini. Prototaip sebenarnya adalah contoh objek. Maksud prototaip ialah: jika pembina mempunyai objek prototaip A, maka contoh yang dibuat oleh pembina mesti disalin daripada A. Memandangkan tika itu disalin daripada objek A, tika itu mesti mewarisi semua sifat A, kaedah dan sifat lain. Jadi, bagaimana replikasi dicapai? Kaedah 1: Penyalinan pembinaan: Setiap kali kejadian dibina, kejadian disalin daripada prototaip Contoh baharu dan prototaip menduduki ruang memori yang sama. Walaupun ini menjadikan obj1 dan obj2 "sepenuhnya konsisten" dengan prototaip mereka, ia juga sangat tidak ekonomik - penggunaan ruang memori akan meningkat dengan cepat. Seperti yang ditunjukkan dalam gambar:
Kaedah 2: Copy-on-write Strategi ini datang daripada teknik yang secara konsisten menipu sistem: copy-on-write. Contoh tipikal penipuan jenis ini ialah perpustakaan pautan dinamik (DDL) dalam sistem pengendalian, yang kawasan memorinya sentiasa disalin semasa menulis. Seperti yang ditunjukkan dalam gambar:
Kami hanya perlu menunjukkan dalam sistem bahawa obj1 dan obj2 adalah sama dengan prototaip mereka, supaya apabila membaca, kami hanya perlu mengikut arahan untuk membaca prototaip. Apabila kita perlu menulis sifat objek (seperti obj2), kita menyalin imej prototaip dan menunjukkan operasi masa hadapan ke imej ini. Seperti yang ditunjukkan dalam gambar:
Kelebihan kaedah ini ialah kami tidak memerlukan banyak overhed memori semasa membuat kejadian dan atribut membaca Kami hanya menggunakan beberapa kod untuk memperuntukkan memori semasa menulis buat kali pertama, yang membawa beberapa kod dan overhed memori. . Tetapi tiada lagi overhead seperti itu, kerana kecekapan mengakses imej adalah sama seperti mengakses prototaip. Namun, bagi sistem yang kerap melakukan operasi tulis, kaedah ini tidak lebih menjimatkan daripada kaedah sebelumnya. Kaedah 3: Baca traversal Kaedah ini menukar butiran replikasi daripada prototaip kepada ahli. Ciri kaedah ini ialah hanya apabila menulis ahli contoh, maklumat ahli disalin ke imej contoh. Apabila menulis atribut objek, contohnya (obj2.value=10), nilai atribut bernama nilai akan dijana dan diletakkan dalam senarai ahli objek obj2. Tengok gambar:
Boleh didapati bahawa obj2 masih merupakan rujukan yang menunjuk kepada prototaip, dan tiada contoh objek yang sama saiz dengan prototaip dibuat semasa operasi. Dengan cara ini, operasi tulis tidak menghasilkan peruntukan memori yang besar, jadi penggunaan memori menjadi menjimatkan. Perbezaannya ialah obj2 (dan semua contoh objek) perlu mengekalkan senarai ahli. Senarai ahli ini mengikut dua peraturan: Ia dijamin untuk diakses terlebih dahulu apabila membaca Jika tiada atribut dinyatakan dalam objek, percubaan dibuat untuk melintasi keseluruhan rantaian prototaip objek sehingga prototaip kosong atau atribut ditemui. Rantaian prototaip akan dibincangkan kemudian. Jelas sekali, antara tiga kaedah tersebut, read traversal mempunyai prestasi terbaik. Oleh itu, warisan prototaip JavaScript dibaca traversal. constructor Orang yang biasa dengan C pasti akan keliru selepas membaca kod objek teratas. Lebih mudah untuk memahami tanpa kata kunci kelas Lagipun, terdapat kata kunci fungsi, yang hanya kata kunci yang berbeza. Tetapi bagaimana dengan pembina? Malah, JavaScript juga mempunyai pembina yang serupa, tetapi ia dipanggil pembina. Apabila menggunakan operator baharu, pembina sebenarnya telah dipanggil dan ini terikat pada objek. Sebagai contoh, kami menggunakan kod berikut
haiwan tidak dapat ditentukan. Sesetengah orang akan mengatakan bahawa tiada nilai pulangan sudah tentu tidak ditentukan. Kemudian jika anda menukar definisi objek Haiwan:
Teka haiwan apakah sekarang?
Pada masa ini, haiwan itu menjadi tetingkap Perbezaannya ialah tetingkap itu dilanjutkan supaya tetingkap itu mempunyai atribut nama. Ini kerana ini lalai kepada tetingkap, yang merupakan pembolehubah peringkat atas, jika ia tidak dinyatakan. Hanya dengan memanggil kata kunci baharu boleh pembina dipanggil dengan betul. Jadi, bagaimana untuk mengelakkan pengguna daripada kehilangan kata kunci baharu? Kita boleh membuat beberapa perubahan kecil:
Dengan cara ini anda akan menjadi kalis mudah. Pembina juga digunakan untuk menunjukkan objek mana yang dimiliki oleh contoh itu. Kita boleh menggunakan instanceof untuk menilai, tetapi instanceof mengembalikan benar untuk kedua-dua objek nenek moyang dan objek sebenar semasa pewarisan, jadi ia tidak sesuai. Apabila pembina dipanggil dengan baru, ia menunjuk ke objek semasa secara lalai.
Kita boleh berfikir secara berbeza: prototaip tidak mempunyai nilai sama sekali apabila fungsi dimulakan mungkin logik berikut
//Tetapkan __proto__ sebagai ahli terbina dalam fungsi dan get_prototyoe() ialah kaedahnya
Kelebihan ini ialah ia mengelakkan mencipta contoh objek setiap kali fungsi diisytiharkan, menjimatkan overhed. Pembina boleh diubah suai, yang akan dibincangkan kemudian. Warisan berasaskan prototaip Saya percaya semua orang tahu hampir apa itu warisan, tetapi saya tidak akan menunjukkan had IQ yang lebih rendah.
Terdapat beberapa jenis warisan JS, berikut adalah dua jenis
1 Kaedah 1 Kaedah ini adalah yang paling biasa digunakan dan mempunyai keselamatan yang lebih baik. Mari kita tentukan dua objek
dahuluUntuk membina pewarisan adalah sangat mudah, arahkan prototaip objek anak kepada contoh objek induk (perhatikan bahawa ia adalah contoh, bukan objek)
Pada masa ini, anjing akan mempunyai dua sifat, nama dan umur. Dan jika anda menggunakan instanceof operator
pada anjingDengan cara ini, warisan dicapai, tetapi ada masalah kecil
Anda dapat melihat bahawa objek yang ditunjukkan oleh pembina telah berubah, yang tidak memenuhi tujuan kami. Kami tidak boleh menilai milik siapa contoh baharu kami. Oleh itu, kita boleh menambah ayat:
Mari kita lihat semula:
selesai. Kaedah ini adalah sebahagian daripada penyelenggaraan rantaian prototaip dan akan diterangkan secara terperinci di bawah. 2. Kaedah 2 Kaedah ini mempunyai kebaikan dan keburukan, tetapi keburukan mengatasi kelebihannya. Mari lihat kod dahulu
function Animal(name) {
This.name = nama;
}
Animal.prototype.setName = fungsi(nama) {
This.name = nama;
}
fungsi Anjing(umur) {
This.age = umur;
}
Dog.prototype = Haiwan.prototaip;
Ini mencapai penyalinan prototaip.
Kelebihan kaedah ini ialah ia tidak perlu membuat instantiate objek (berbanding kaedah 1), yang menjimatkan sumber. Kelemahannya juga jelas Di samping masalah yang sama seperti di atas, iaitu, pembina menunjuk ke objek induk, ia hanya boleh menyalin sifat dan kaedah yang diisytiharkan oleh objek induk menggunakan prototaip. Dalam erti kata lain, dalam kod di atas, atribut nama objek Haiwan tidak boleh disalin, tetapi kaedah setName boleh disalin. Perkara yang paling mematikan ialah sebarang pengubahsuaian kepada prototaip objek kanak-kanak akan menjejaskan prototaip objek induk, iaitu, kejadian yang diisytiharkan oleh kedua-dua objek akan terjejas. Oleh itu, kaedah ini tidak digalakkan.
Rantai Prototaip
Setiap orang yang mempunyai warisan bertulis tahu bahawa warisan boleh diwarisi pada pelbagai peringkat. Dalam JS, ini membentuk rantaian prototaip. Rantai prototaip telah disebut berkali-kali di atas, jadi apakah rantai prototaip itu? Satu contoh hendaklah sekurang-kurangnya mempunyai atribut proto yang menunjuk kepada prototaip, yang merupakan asas sistem objek dalam JavaScript. Walau bagaimanapun, sifat ini tidak kelihatan. Kami memanggilnya sebagai "rantai prototaip dalaman" untuk membezakannya daripada "rantai prototaip pembina" yang terdiri daripada prototaip pembina (yang biasanya kami panggil "rantai prototaip"). Mula-mula mari kita bina hubungan warisan mudah mengikut kod di atas:
Sebagai peringatan, seperti yang dinyatakan sebelum ini, semua objek mewarisi objek kosong. Oleh itu, kami membina rantai prototaip:
Kita dapat melihat bahawa prototaip objek kanak-kanak menghala ke contoh objek induk, membentuk rantai prototaip pembina. Objek proto dalaman bagi tika anak juga menunjuk kepada contoh objek induk, membentuk rantai prototaip dalaman. Apabila kita perlu mencari atribut tertentu, kod itu serupa dengan
Wenn wir in diesem Beispiel nach dem Namensattribut in Hund suchen, wird es in der Mitgliederliste in Hund gesucht. Natürlich wird es nicht gefunden, da die Mitgliederliste von Hund jetzt nur das Alter enthält. Anschließend wird die Suche entlang der Prototypenkette fortgesetzt, dh der Instanz, auf die .proto zeigt, dh im Tier wird das Namensattribut gefunden und zurückgegeben. Wenn nach einer Eigenschaft gesucht wird, die nicht existiert, und diese in animal nicht gefunden werden kann, wird die Suche entlang von .proto fortgesetzt und ein leeres Objekt gefunden. Wenn es diese nicht finden kann, wird die Suche in .proto fortgesetzt leeres Objekt. Proto zeigt auf Null und sucht nach Ausgang.
Wartung der Prototypenkette Wir haben eine Frage aufgeworfen, als wir gerade über die Prototypenvererbung gesprochen haben. Bei der Verwendung von Methode 1 zum Erstellen der Vererbung zeigt der Konstruktor der untergeordneten Objektinstanz auf das übergeordnete Objekt. Dies hat den Vorteil, dass wir über das Konstruktorattribut auf die Prototypenkette zugreifen können, aber auch die Nachteile liegen auf der Hand. Ein Objekt, dessen Instanz es generiert, sollte auf sich selbst zeigen, also
Es gibt jedoch noch zwei weitere Probleme:
__proto__ ist überschreibbar, was bedeutet, dass bei der Verwendung immer noch Risiken bestehen
__proto__ ist eine spezielle Verarbeitung von SpiderMonkey und kann nicht in anderen Engines (wie JScript) verwendet werden.
Wir haben auch eine andere Möglichkeit, nämlich die Konstruktoreigenschaften des Prototyps beizubehalten und die Konstruktoreigenschaften der Instanz innerhalb der Konstruktorfunktion der Unterklasse zu initialisieren.
Der Code lautet wie folgt: Schreiben Sie das Unterobjekt neu