ES6 memungkinkan untuk memperluas objek khusus. Jadi dimungkinkan untuk mewarisi dari fungsinya. Objek seperti itu dapat disebut sebagai fungsi, tetapi bagaimana cara mengimplementasikan logika untuk panggilan tersebut?
class Smth extends Function {
constructor (x) {
// What should be done here
super();
}
}
(new Smth(256))() // to get 256 at this call?
Setiap metode kelas mendapat referensi ke instance kelas melalui this
. Tapi ketika itu disebut sebagai fungsi, this
mengacu pada window
. Bagaimana saya bisa mendapatkan referensi ke instance kelas ketika itu disebut sebagai fungsi?
super(x)
(yaitu menyebarkannya keFunction
)? Tidak yakin apakahFunction
benar-benar dapat diperpanjang.Error
, antara lain.Function
ini hanyalah konstruktor fungsi. Implementasi fungsi harus diteruskan ke konstruktor. Jika Anda tidak inginSmth
menerima implementasi, Anda harus menyediakannya di konstruktor, yaitusuper('function implementation here')
.Function
konstruktor (runtime) yang sangat berbeda dari ekspresi fungsi (sintaks).Jawaban:
The
super
panggilan akan memanggilFunction
konstruktor, yang mengharapkan string kode. Jika Anda ingin mengakses data instance Anda, Anda dapat membuat hardcode:tapi itu tidak terlalu memuaskan. Kami ingin menggunakan penutupan.
Memiliki fungsi yang dikembalikan menjadi closure yang dapat mengakses variabel instan Anda adalah mungkin, tetapi tidak mudah. Hal baiknya adalah Anda tidak perlu memanggil
super
jika Anda tidak mau - Anda masih dapatreturn
mengubah objek dari konstruktor kelas ES6 Anda. Dalam hal ini, kami akan melakukannyaTetapi kita dapat melakukan lebih baik lagi, dan mengabstraksikan hal ini dari
Smth
:Memang, ini menciptakan tingkat tipuan tambahan dalam rantai pewarisan, tetapi itu belum tentu merupakan hal yang buruk (Anda dapat memperpanjangnya alih-alih yang asli
Function
). Jika Anda ingin menghindarinya, gunakantetapi perhatikan bahwa
Smth
tidak akan secara dinamis mewarisiFunction
properti statis .sumber
Smth
instance Anda menjadiinstanceof Smth
(seperti yang diharapkan semua orang). Anda dapat menghilangkanObject.setPrototypeOf
panggilan jika Anda tidak memerlukan metode ini atau metode prototipe apa pun yang dideklarasikan di kelas Anda.Object.setPrototypeOf
tidak banyak bahaya pengoptimalan selama itu dilakukan tepat setelah membuat objek. Hanya jika Anda memutasikan [[prototipe]] suatu objek bolak-balik selama masa pakainya, hal itu akan berdampak buruk.this
danreturn
objek.Ini adalah pendekatan untuk membuat objek yang dapat dipanggil yang mereferensikan anggota objeknya dengan benar, dan mempertahankan pewarisan yang benar, tanpa mengotak-atik prototipe.
Secara sederhana:
Perluas kelas ini dan tambahkan
__call__
metode, selengkapnya di bawah ...Penjelasan dalam kode dan komentar:
Lihat di repl.it
Penjelasan lebih lanjut tentang
bind
:function.bind()
berfungsi sepertifunction.call()
, dan mereka berbagi tanda tangan metode serupa:fn.call(this, arg1, arg2, arg3, ...);
lebih lanjut tentang mdnfn.bind(this, arg1, arg2, arg3, ...);
lebih lanjut tentang mdnDi kedua argumen pertama mendefinisikan ulang
this
konteks di dalam fungsi. Argumen tambahan juga bisa diikat ke nilai. Tapi di manacall
segera memanggil fungsi dengan nilai terikat,bind
mengembalikan objek fungsi "eksotis" yang secara transparan membungkus aslinya, denganthis
dan semua argumen preset.Jadi ketika Anda mendefinisikan suatu fungsi maka
bind
beberapa argumennya:Anda memanggil fungsi terikat dengan hanya argumen yang tersisa, konteksnya telah diatur sebelumnya, dalam hal ini ke
['hello']
.sumber
bind
berhasil (yaitu mengapa mengembalikan contohExFunc
)?bind
mengembalikan objek fungsi transparan yang membungkus objek fungsi yang dipanggilnya, yang merupakan objek yang dapat dipanggil, hanya denganthis
konteks rebound. Jadi itu benar-benar mengembalikan contoh yang dibungkus secara transparanExFunc
. Posting diperbarui dengan info lebih lanjut tentangbind
.constructor
setelahbind
dalamExFunc
. Di subclass ExFunc, semua anggota dapat diakses. Adapuninstanceof
; di es6 fungsi terikat disebut sebagai eksotis, jadi cara kerja dalamnya tidak terlihat, tapi saya pikir itu meneruskan panggilan ke target yang dibungkus, viaSymbol.hasInstance
. Ini seperti Proxy, tetapi ini adalah metode sederhana untuk mencapai efek yang diinginkan. Tanda tangan mereka mirip tidak sama.__call__
saya tidak dapat mengaksesthis.a
atauthis.ab()
. mis. repl.it/repls/FelineFinishedDesktopenvironmentAnda dapat menggabungkan instance Smth dalam Proxy dengan perangkap
apply
(dan mungkinconstruct
):sumber
this
masih merupakan fungsi kosong (centangnew Smth().toString()
).setPrototypeOf
dan tidak mengatakan apa pun tentang proxy. Tapi saya kira proxy bisa sama bermasalahnya dengansetPrototypeOf
. Dan tentangtoString
, itu bisa dibayangi dengan metode kustom diSmth.prototype
. Yang asli tergantung pada implementasi.construct
jebakan untuk menentukan perilakunyanew new Smth(256)()
. Dan tambahkan metode khusus yang membayangi metode asli yang mengakses kode suatu fungsi, seperti yangtoString
dicatat Bergi.apply
metode Anda yang diterapkan dengan cara yang seharusnya digunakan, atau ini hanya demonstrasi dan saya perlu melihat lebih banyak informasi tentangProxy
danReflect
menggunakannya dengan cara yang benar?Saya mengambil saran dari jawaban Bergi dan membungkusnya menjadi modul NPM .
sumber
Memperbarui:
Sayangnya ini tidak cukup berfungsi karena sekarang mengembalikan objek fungsi alih-alih kelas, jadi tampaknya ini sebenarnya tidak dapat dilakukan tanpa memodifikasi prototipe. Kuno.
Pada dasarnya masalahnya adalah tidak ada cara untuk mengatur
this
nilaiFunction
konstruktor. Satu-satunya cara untuk benar-benar melakukan ini adalah dengan menggunakan.bind
metode setelahnya, namun ini tidak terlalu ramah Kelas.Kita bisa melakukan ini di kelas dasar helper, namun
this
tidak tersedia sampai setelahsuper
panggilan awal , jadi ini agak rumit.Contoh Kerja:
(Contoh membutuhkan browser modern atau
node --harmony
.)Pada dasarnya fungsi dasar
ClassFunction
extends akan membungkusFunction
panggilan konstruktor dengan fungsi kustom yang mirip dengan.bind
, tetapi memungkinkan pengikatan nanti, pada panggilan pertama. Kemudian diClassFunction
konstruktor itu sendiri, ia memanggil fungsi yang dikembalikansuper
yang sekarang menjadi fungsi terikat, meneruskanthis
untuk menyelesaikan penyiapan fungsi bind kustom.Ini semua agak rumit, tetapi itu menghindari mutasi prototipe, yang dianggap bentuk buruk untuk alasan pengoptimalan dan dapat menghasilkan peringatan di konsol browser.
sumber
bound
akan merujuk ke fungsi yang Andareturn
dari kelas anonim itu. Sebut saja, dan lihat langsung. Saya juga akan merekomendasikan untuk menghindari melewatkan string kode, mereka hanya berantakan untuk dikerjakan (dalam setiap langkah proses pengembangan).extends
tidak benar-benar berfungsi seperti yang diharapkan, karenaFunction.isPrototypeOf(Smth)
dan juganew Smth instanceof Function
salah.console.log((new Smth) instanceof Function);
adalahtrue
bagi saya dalam Node v5.11.0 dan Firefox terbaru.new Smth instanceof Smth
tidak berhasil dengan solusi Anda. Juga tidak ada metode yangSmth
akan tersedia pada instans Anda - karena Anda hanya mengembalikan standarFunction
, bukan fileSmth
.extend Function
juga membuatnew Smth instanceof Smth
palsu.Pertama saya datang ke solusi dengan
arguments.callee
, tapi itu mengerikan.Saya mengharapkannya untuk istirahat dalam mode ketat global, tetapi sepertinya itu berfungsi bahkan di sana.
Itu cara yang buruk karena menggunakan
arguments.callee
, meneruskan kode sebagai string dan memaksa eksekusinya dalam mode non-ketat. Tapi ide untuk mengesampingkanapply
muncul.Dan pengujiannya, menunjukkan saya dapat menjalankan ini sebagai fungsi dengan cara yang berbeda:
Versi dengan
sebenarnya mengandung
bind
fungsionalitas:Versi dengan
membuat
call
danapply
padawindow
tidak konsisten:jadi cek harus dipindahkan ke
apply
:sumber
this
adalah jendela, tidak ditentukan, jadi fungsi yang dibuat tidak dalam mode ketat (setidaknya di chrome).Function
tidak dalam mode ketat. Meski mengerikan, ini menarik +1. Anda mungkin tidak akan bisa berjalan lebih jauh.Ini adalah solusi yang telah saya kerjakan yang melayani semua kebutuhan saya akan fungsi perluasan dan telah melayani saya dengan cukup baik. Manfaat dari teknik ini adalah:
ExtensibleFunction
, kode ini idiomatis untuk memperluas setiap kelas ES6 (tidak, menyia-nyiakan dengan konstruktor atau proxy yang berpura-pura).instanceof
/.constructor
mengembalikan nilai yang diharapkan..bind()
.apply()
dan.call()
semua berfungsi seperti yang diharapkan. Ini dilakukan dengan menimpa metode ini untuk mengubah konteks fungsi "dalam" sebagai lawan dariExtensibleFunction
instance (atau subkelasnya)..bind()
mengembalikan instance baru dari konstruktor fungsi (baik ituExtensibleFunction
atau subclass). Ini digunakanObject.assign()
untuk memastikan properti yang disimpan di fungsi terikat konsisten dengan yang ada di fungsi asal.Symbol
, yang dapat dikaburkan oleh modul atau IIFE (atau teknik umum lainnya untuk referensi privatisasi).Dan tanpa basa-basi lagi, kodenya:
Edit
Karena saya sedang mood, saya pikir saya akan menerbitkan paket untuk ini di npm.
sumber
Ada solusi sederhana yang memanfaatkan kemampuan fungsional JavaScript: Teruskan "logika" sebagai argumen fungsi ke konstruktor kelas Anda, tetapkan metode kelas tersebut ke fungsi itu, lalu kembalikan fungsi itu dari konstruktor sebagai hasilnya :
Di atas telah diuji pada Node.js 8.
Kelemahan dari contoh di atas adalah tidak mendukung metode yang diwarisi dari rantai superkelas. Untuk mendukung itu, cukup ganti "Object. GetOwnPropertyNames (...)" dengan sesuatu yang juga mengembalikan nama metode yang diturunkan. Bagaimana melakukan itu saya yakin dijelaskan dalam beberapa pertanyaan-jawaban lain di Stack Overflow :-). BTW. Akan lebih baik jika ES7 menambahkan metode untuk menghasilkan nama metode yang diturunkan juga ;-).
Jika Anda perlu mendukung metode yang diwariskan, satu kemungkinan adalah menambahkan metode statis ke kelas di atas yang mengembalikan semua nama metode yang diwariskan dan lokal. Kemudian panggil itu dari konstruktor. Jika Anda kemudian memperluas kelas Funk itu, Anda mendapatkan metode statis yang diwarisi juga.
sumber