Saya telah sampai pada titik di mana saya perlu memiliki beberapa pewarisan berganda yang belum sempurna yang terjadi di JavaScript. (Saya di sini bukan untuk membahas apakah ini ide yang bagus atau tidak, jadi tolong simpan komentar itu untuk Anda sendiri.)
Saya hanya ingin tahu apakah ada yang mencoba ini dengan sukses (atau tidak), dan bagaimana mereka melakukannya.
Untuk mendidihkannya, yang benar-benar saya butuhkan adalah untuk dapat memiliki objek yang mampu mewarisi properti dari lebih dari satu rantai prototipe (yaitu masing-masing prototipe dapat memiliki rantai yang tepat sendiri), tetapi dalam urutan prioritas tertentu (itu akan cari rantai untuk definisi pertama).
Untuk menunjukkan bagaimana ini secara teori dimungkinkan, itu dapat dicapai dengan menempelkan rantai sekunder pada akhir rantai primer, tetapi ini akan memengaruhi semua contoh prototipe sebelumnya dan bukan itu yang saya inginkan.
Pikiran?
Jawaban:
Berbagai pewarisan dapat dicapai dalam ECMAScript 6 dengan menggunakan objek Proxy .
Penerapan
Penjelasan
Objek proxy terdiri dari objek target dan beberapa jebakan, yang menentukan perilaku khusus untuk operasi mendasar.
Saat membuat objek yang diwarisi dari yang lain, kami menggunakan
Object.create(obj)
. Tetapi dalam hal ini kami ingin pewarisan berganda, jadi alih-alihobj
saya menggunakan proxy yang akan mengarahkan operasi mendasar ke objek yang sesuai.Saya menggunakan perangkap ini:
has
perangkap adalah perangkap bagiin
operator yang . Saya menggunakansome
untuk memeriksa apakah setidaknya satu prototipe berisi properti.get
perangkap adalah perangkap untuk mendapatkan nilai properti. Saya menggunakanfind
untuk menemukan prototipe pertama yang berisi properti itu, dan saya mengembalikan nilainya, atau memanggil pengambil pada penerima yang sesuai. Ini ditangani olehReflect.get
. Jika tidak ada prototipe yang mengandung properti, saya kembaliundefined
.set
perangkap adalah perangkap untuk menetapkan nilai properti. Saya menggunakanfind
untuk menemukan prototipe pertama yang berisi properti itu, dan saya menyebutnya setter pada penerima yang sesuai. Jika tidak ada setter atau prototipe tidak berisi properti, nilai ditentukan pada penerima yang sesuai. Ini ditangani olehReflect.set
.enumerate
perangkap adalah perangkap untukfor...in
loop . Saya beralih properti enumerable dari prototipe pertama, lalu dari yang kedua, dan seterusnya. Setelah sebuah properti di iterasi, saya menyimpannya di tabel hash untuk menghindari iterasi lagi.Peringatan : Perangkap ini telah dihapus dalam konsep ES7 dan tidak digunakan lagi di browser.
ownKeys
perangkap adalah perangkap bagiObject.getOwnPropertyNames()
. Sejak ES7,for...in
loop terus memanggil [[GetPrototypeOf]] dan mendapatkan properti masing-masing. Jadi untuk membuatnya mengulangi properti dari semua prototipe, saya menggunakan perangkap ini untuk membuat semua properti yang diwarisi enumerable muncul seperti properti sendiri.getOwnPropertyDescriptor
perangkap adalah perangkap bagiObject.getOwnPropertyDescriptor()
. Membuat semua properti enumerable tampak seperti properti sendiri dalamownKeys
perangkap tidak cukup,for...in
loop akan meminta deskriptor untuk memeriksa apakah mereka enumerable. Jadi saya gunakanfind
untuk menemukan prototipe pertama yang berisi properti itu, dan saya mengulangi rantai prototipenya sampai saya menemukan pemilik properti, dan saya mengembalikan deskriptornya. Jika tidak ada prototipe yang mengandung properti, saya kembaliundefined
. Deskriptor dimodifikasi untuk membuatnya dapat dikonfigurasi, jika tidak kita dapat memecah beberapa invarian proxy.preventExtensions
dandefineProperty
hanya disertakan untuk mencegah operasi ini dari memodifikasi target proxy. Kalau tidak, kita bisa akhirnya memecahkan beberapa invarian proxy.Ada lebih banyak jebakan yang tersedia, yang tidak saya gunakan
getPrototypeOf
perangkap bisa ditambahkan, tetapi tidak ada cara yang tepat untuk mengembalikan beberapa prototipe. Ini berartiinstanceof
tidak akan berhasil. Oleh karena itu, saya membiarkannya mendapatkan prototipe target, yang awalnya adalah nol.setPrototypeOf
perangkap dapat ditambahkan dan menerima array obyek, yang akan menggantikan prototipe. Ini dibiarkan sebagai latihan untuk pembaca. Di sini saya hanya membiarkannya memodifikasi prototipe target, yang tidak banyak berguna karena tidak ada perangkap yang menggunakan target.deleteProperty
perangkap adalah perangkap bagi menghapus sifat sendiri. Proxy mewakili warisan, jadi ini tidak masuk akal. Saya membiarkannya mencoba penghapusan pada target, yang seharusnya tidak memiliki properti.isExtensible
perangkap adalah perangkap untuk mendapatkan diperpanjang tersebut. Tidak banyak berguna, mengingat bahwa seorang invarian memaksa untuk mengembalikan ekstensibilitas yang sama dengan target. Jadi saya biarkan saja mengarahkan operasi ke target, yang akan diperluas.apply
danconstruct
perangkap perangkap untuk menelepon atau instantiating. Mereka hanya berguna ketika targetnya adalah fungsi atau konstruktor.Contoh
sumber
multiInherit(o1={a:1}, o2={b:2}, o3={a:3, b:3})
Object.assign
) atau mendapatkan grafik yang sangat berbeda, pada akhirnya semuanya mendapatkan rantai prototipe satu-satunya antara objek. Solusi proxy menawarkan percabangan runtime, dan ini mengguncang!Pembaruan (2019): Posting asli semakin usang. Artikel ini (sekarang tautan arsip internet, karena domain hilang) dan pustaka GitHub yang terkait adalah pendekatan modern yang bagus.
Posting asli: Berbagai warisan [sunting, bukan jenis warisan yang tepat, tetapi properti; mixins] dalam Javascript cukup mudah jika Anda menggunakan prototipe buatan daripada yang generik-objek. Berikut adalah dua kelas induk untuk diwarisi dari:
Perhatikan bahwa saya telah menggunakan anggota "nama" yang sama dalam setiap kasus, yang bisa menjadi masalah jika orang tua tidak setuju tentang bagaimana "nama" harus ditangani. Tapi mereka kompatibel (berlebihan, sungguh) dalam hal ini.
Sekarang kita hanya perlu kelas yang mewarisi dari keduanya. Warisan dilakukan dengan memanggil fungsi konstruktor (tanpa menggunakan kata kunci baru) untuk prototipe dan konstruktor objek. Pertama, prototipe harus mewarisi dari prototipe induk
Dan konstruktor harus mewarisi dari konstruktor induk:
Sekarang Anda dapat menanam, makan, dan memanen berbagai contoh:
sumber
Array.call(...)
tetapi sepertinya tidak mempengaruhi apa pun yang saya lewatithis
.Array.prototype.constructor.call()
Ini digunakan
Object.create
untuk membuat rantai prototipe nyata:Sebagai contoh:
akan kembali:
sehingga
obj.a === 1
,obj.b === 3
, dllsumber
Saya suka implementasi John Resig atas struktur kelas: http://ejohn.org/blog/simple-javascript-inheritance/
Ini dapat dengan mudah diperluas ke sesuatu seperti:
yang akan memungkinkan Anda untuk melewati beberapa objek yang mewarisi. Anda akan kehilangan
instanceOf
kemampuan di sini, tapi itu diberikan jika Anda ingin banyak warisan.contoh saya yang agak berbelit-belit di atas tersedia di https://github.com/cwolves/Fetch/blob/master/support/plugins/klass/klass.js
Perhatikan bahwa ada beberapa kode mati dalam file itu, tetapi memungkinkan beberapa pewarisan jika Anda ingin melihatnya.
Jika Anda ingin warisan yang dirantai (BUKAN multiple inheritance, tetapi bagi kebanyakan orang itu adalah hal yang sama), itu dapat diselesaikan dengan Kelas seperti:
yang akan mempertahankan rantai prototipe asli, tetapi Anda juga akan memiliki banyak kode tidak berguna yang berjalan.
sumber
Jangan bingung dengan implementasi kerangka kerja JavaScript dari multiple inheritance.
Yang perlu Anda lakukan adalah menggunakan Object.create () untuk membuat objek baru setiap kali dengan objek dan properti prototipe yang ditentukan, kemudian pastikan untuk mengubah Object.prototype.constructor setiap langkah dari jalan jika Anda berencana untuk instantiating
B
di masa depan.Untuk mewarisi properti instance
thisA
danthisB
kami menggunakan Function.prototype.call () di akhir setiap fungsi objek. Ini opsional jika Anda hanya peduli mewarisi prototipe.Jalankan kode berikut di suatu tempat dan amati
objC
:B
mewarisi prototipe dariA
C
mewarisi prototipe dariB
objC
adalah contoh dariC
Ini adalah penjelasan yang baik dari langkah-langkah di atas:
OOP Dalam JavaScript: Yang Harus Anda Ketahui
sumber
Saya sama sekali tidak ahli dalam javascript OOP, tetapi jika saya mengerti Anda dengan benar Anda menginginkan sesuatu seperti (pseudo-code):
Dalam hal ini, saya akan mencoba sesuatu seperti:
sumber
c.prototype
beberapa kali tidak menghasilkan banyak prototipe. Misalnya, jika Anda punyaAnimal.isAlive = true
,Cat.isAlive
akan tetap tidak terdefinisi.Dimungkinkan untuk menerapkan banyak pewarisan dalam JavaScript, meskipun sangat sedikit perpustakaan yang melakukannya.
Saya bisa menunjuk Ring.js , satu-satunya contoh yang saya tahu.
sumber
Saya banyak mengerjakan hal ini hari ini dan mencoba mencapai ini sendiri di ES6. Cara saya melakukannya menggunakan Browserify, Babel dan kemudian saya mengujinya dengan Wallaby dan sepertinya berhasil. Tujuan saya adalah untuk memperpanjang Array saat ini, termasuk ES6, ES7 dan menambahkan beberapa fitur kustom tambahan yang saya butuhkan dalam prototipe untuk menangani data audio.
Wallaby melewati 4 tes saya. File example.js dapat di-paste di konsol dan Anda dapat melihat bahwa properti 'include' ada dalam prototipe kelas. Saya masih ingin menguji ini lagi besok.
Inilah metode saya: (Saya kemungkinan besar akan refactor dan repackage sebagai modul setelah tidur!)
Github Repo: https://github.com/danieldram/array-includes-polyfill
sumber
Saya pikir ini sangat sederhana. Masalahnya di sini adalah bahwa kelas anak hanya akan merujuk
instanceof
untuk kelas pertama yang Anda panggilhttps://jsfiddle.net/1033xzyt/19/
sumber
Periksa kode di bawah ini yang menunjukkan dukungan untuk pewarisan berganda. Dilakukan dengan menggunakan Warisan PROTOTIPAL
sumber
Saya memiliki cukup fungsi untuk memungkinkan kelas didefinisikan dengan multiple inheritance. Ini memungkinkan untuk kode seperti berikut. Secara keseluruhan Anda akan mencatat keberangkatan lengkap dari teknik Klasifikasi asli dalam javascript (mis. Anda tidak akan pernah melihat
class
kata kunci):untuk menghasilkan output seperti ini:
Berikut adalah definisi kelasnya:
Kita dapat melihat bahwa setiap definisi kelas menggunakan
makeClass
fungsi menerimaObject
nama kelas induk yang dipetakan ke kelas induk. Ini juga menerima fungsi yang mengembalikanObject
properti yang mengandung untuk kelas yang sedang didefinisikan. Fungsi ini memiliki parameterprotos
, yang berisi informasi yang cukup untuk mengakses properti apa pun yang ditentukan oleh kelas induknya.Bagian terakhir yang diperlukan adalah
makeClass
fungsi itu sendiri, yang melakukan sedikit pekerjaan. Ini dia, bersama dengan sisa kode. Saya sudah berkomentarmakeClass
cukup banyak:The
makeClass
Fungsi juga mendukung properti kelas; ini didefinisikan dengan awalan nama properti dengan$
simbol (perhatikan bahwa nama properti akhir yang hasilnya akan$
dihapus). Dengan mengingat hal ini, kita dapat menulisDragon
kelas khusus yang memodelkan "tipe" Naga, di mana daftar tipe Naga yang tersedia disimpan di Kelas itu sendiri, sebagai lawan dari contoh:Tantangan Berbagai Warisan
Siapa pun yang mengikuti kode untuk
makeClass
erat akan mencatat fenomena yang tidak diinginkan yang agak signifikan terjadi secara diam-diam ketika kode di atas berjalan: instantiatingRunningFlying
akan menghasilkan DUA panggilan keNamed
konstruktor!Ini karena grafik warisan terlihat seperti ini:
Ketika ada beberapa lintasan ke kelas induk yang sama dalam grafik warisan kelas-sub , instantiations dari sub-kelas akan memanggil konstruktor kelas induk beberapa kali.
Memerangi ini tidak sepele. Mari kita lihat beberapa contoh dengan nama kelas yang disederhanakan. Kami akan mempertimbangkan kelas
A
, kelas induk yang paling abstrak, kelasB
danC
, yang keduanya mewarisi dariA
, dan kelasBC
yang mewarisi dariB
danC
(dan karenanya secara konseptual "warisan ganda" dariA
):Jika kita ingin mencegah permintaan
BC
ganda,A.prototype.init
kita mungkin perlu meninggalkan gaya memanggil langsung konstruktor yang diwariskan. Kita akan memerlukan beberapa tingkat tipuan untuk memeriksa apakah panggilan duplikat terjadi, dan korsleting sebelum terjadi.Kami dapat mempertimbangkan untuk mengubah parameter yang disediakan ke fungsi properti: di samping
protos
,Object
data mentah berisi yang menggambarkan properti yang diwariskan, kami juga dapat menyertakan fungsi utilitas untuk memanggil metode contoh sedemikian rupa sehingga metode induk juga dipanggil, tetapi panggilan duplikat terdeteksi dan dicegah. Mari kita lihat di mana kita menetapkan parameter untukpropertiesFn
Function
:Seluruh tujuan perubahan di atas
makeClass
adalah agar kami memiliki argumen tambahan yang diberikan kepada kamipropertiesFn
saat kami memohonmakeClass
. Kita juga harus menyadari bahwa setiap fungsi yang didefinisikan dalam kelas apa pun sekarang dapat menerima parameter setelah semua yang lain, dinamaidup
, yang merupakanSet
yang menyimpan semua fungsi yang telah dipanggil sebagai akibat dari memanggil metode yang diwarisi:Gaya baru ini sebenarnya berhasil memastikan
"Construct A"
hanya dicatat satu kali ketika sebuah instanceBC
diinisialisasi. Tetapi ada tiga kelemahan, yang ketiga sangat kritis :util.invokeNoDuplicates
fungsi, dan berpikir tentang bagaimana gaya ini menghindari multi-doa adalah non-intuitif dan merangsang sakit kepala. Kami juga memilikidups
parameter sial itu , yang benar-benar perlu didefinisikan pada setiap fungsi tunggal di kelas . Aduh.NiftyClass
mengabaikan fungsiniftyFunction
, dan menggunakannyautil.invokeNoDuplicates(this, 'niftyFunction', ...)
untuk menjalankannya tanpa duplikasi-pemanggilan,NiftyClass.prototype.niftyFunction
akan memanggil fungsi yang dinamainiftyFunction
setiap kelas induk yang mendefinisikannya, mengabaikan nilai pengembalian dari kelas-kelas itu, dan akhirnya melakukan logika khususNiftyClass.prototype.niftyFunction
. Ini adalah satu - satunya struktur yang mungkin . JikaNiftyClass
mewarisiCoolClass
danGoodClass
, dan kedua kelas induk ini memberikanniftyFunction
definisi sendiri,NiftyClass.prototype.niftyFunction
tidak akan pernah (tanpa mempertaruhkan banyak pemanggilan) dapat:NiftyClass
pertama, lalu logika khusus kelas indukNiftyClass
pada titik mana pun selain setelah semua logika induk khusus telah selesainiftyFunction
sama sekaliTentu saja, kami dapat menyelesaikan setiap masalah dengan huruf di atas dengan mendefinisikan fungsi khusus di bawah
util
:util.invokeNoDuplicatesSubClassLogicFirst(instance, fnName, ...)
util.invokeNoDuplicatesSubClassAfterParent(parentName, instance, fnName, ...)
(Di manaparentName
nama induk yang logika khususnya akan segera diikuti oleh logika khusus kelas anak-anak)util.invokeNoDuplicatesCanShortCircuitOnParent(parentName, testFn, instance, fnName, ...)
(Dalam hal initestFn
akan menerima hasil dari logika khusus untuk orang tua yang bernamaparentName
, dan akan mengembalikantrue/false
nilai yang menunjukkan apakah korsleting harus terjadi)util.invokeNoDuplicatesBlackListedParents(blackList, instance, fnName, ...)
(Dalam hal iniblackList
akan menjadiArray
nama induk yang logikanasinya harus dilewati sama sekali)Semua solusi ini tersedia, tetapi ini adalah kekacauan total ! Untuk setiap struktur unik yang dapat diambil oleh panggilan fungsi yang diwarisi, kita membutuhkan metode khusus yang didefinisikan di bawah
util
. Benar-benar bencana yang absolut.Dengan mengingat hal ini, kita dapat mulai melihat tantangan dalam menerapkan pewarisan berganda yang baik. Implementasi penuh dari yang
makeClass
saya berikan dalam jawaban ini bahkan tidak mempertimbangkan masalah seruan berganda, atau banyak masalah lain yang timbul berkenaan dengan multiple inheritance.Jawaban ini semakin panjang. Saya harap
makeClass
implementasi yang saya masukkan masih bermanfaat, meskipun tidak sempurna. Saya juga berharap siapa pun yang tertarik dengan topik ini mendapatkan lebih banyak konteks untuk diingat ketika mereka membaca lebih lanjut!sumber
Lihatlah paket IeUnit .
Asimilasi konsep yang diterapkan di IeUnit tampaknya menawarkan apa yang Anda cari dengan cara yang cukup dinamis.
sumber
Berikut adalah contoh rantai prototipe menggunakan fungsi konstruktor :
Konsep ini menggunakan definisi Yehuda Katz tentang "kelas" untuk JavaScript:
Berbeda dengan pendekatan Object.create , ketika kelas dibangun dengan cara ini dan kami ingin membuat contoh "kelas", kita tidak perlu tahu dari mana "kelas" masing-masing mewarisi dari. Kami hanya menggunakan
new
.Urutan prioritas harus masuk akal. Pertama terlihat pada objek instan, lalu prototipe, lalu prototipe berikutnya, dll.
Kita juga dapat memodifikasi prototipe yang akan mempengaruhi semua objek yang dibangun di kelas.
Saya awalnya menulis ini dengan jawaban ini .
sumber
child
Mewarisi dariparent1
danparent2
). Contoh Anda hanya berbicara tentang satu rantai.Seorang latecomer dalam adegan ini adalah SimpleDeclare . Namun, ketika berhadapan dengan banyak pewarisan, Anda masih akan mendapatkan salinan konstruktor asli. Itu keharusan di Javascript ...
Merc.
sumber
Saya akan menggunakan ds.oop . Mirip dengan prototype.js dan lainnya. membuat multiple inheritance sangat mudah dan minimalis. (hanya 2 atau 3 kb) Juga mendukung beberapa fitur rapi lainnya seperti antarmuka dan injeksi ketergantungan
sumber
Bagaimana dengan ini, ini mengimplementasikan beberapa warisan dalam JavaScript:
Dan inilah kode untuk fungsi utilitas spesialisasi_with ():
Ini adalah kode nyata yang berjalan. Anda dapat menyalin-menempelkannya di file html, dan coba sendiri. Itu berhasil.
Itulah upaya untuk mengimplementasikan MI dalam JavaScript. Tidak banyak kode, lebih banyak pengetahuan.
Silakan melihat artikel lengkap saya tentang ini, https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS
sumber
Saya hanya biasa menetapkan kelas apa yang saya butuhkan di properti orang lain, dan menambahkan proxy ke titik-otomatis yang mereka sukai:
sumber