Apakah ada cara untuk membuat variabel "pribadi" (yang didefinisikan dalam konstruktor), tersedia untuk metode yang didefinisikan prototipe?
TestClass = function(){
var privateField = "hello";
this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};
Ini bekerja:
t.nonProtoHello()
Tetapi ini tidak:
t.prototypeHello()
Saya terbiasa mendefinisikan metode saya di dalam konstruktor, tetapi saya menjauh dari itu karena beberapa alasan.
javascript
private-members
kode morgancodes
sumber
sumber
Jawaban:
Tidak, tidak ada cara untuk melakukannya. Itu pada dasarnya adalah pelingkupan terbalik.
Metode yang didefinisikan di dalam konstruktor memiliki akses ke variabel pribadi karena semua fungsi memiliki akses ke ruang lingkup di mana mereka didefinisikan.
Metode yang didefinisikan pada prototipe tidak didefinisikan dalam ruang lingkup konstruktor, dan tidak akan memiliki akses ke variabel lokal konstruktor.
Anda masih dapat memiliki variabel pribadi, tetapi jika Anda ingin metode yang didefinisikan pada prototipe untuk memiliki akses kepada mereka, Anda harus mendefinisikan getter dan setter di
this
objek, yang metode prototipe (bersama dengan segala sesuatu yang lain) akan memiliki akses ke. Sebagai contoh:sumber
secret
properti darithis
. JavaScript tidak mendukung variabel pribadi dengan prototipe karena prototipe terikat pada konteks situs panggilan, bukan konteks situs pembuatan.person.getSecret()
?Pembaruan: Dengan ES6, ada cara yang lebih baik:
Singkatnya, Anda bisa menggunakan yang baru
Symbol
untuk membuat bidang pribadi.Berikut ini deskripsi yang bagus: https://curiosity-driven.org/private-properties-in-javascript
Contoh:
Untuk semua browser modern dengan ES5:
Anda bisa menggunakan Penutup saja
Cara paling sederhana untuk membangun objek adalah dengan menghindari warisan prototipal sama sekali. Cukup tentukan variabel pribadi dan fungsi publik dalam penutupan, dan semua metode publik akan memiliki akses pribadi ke variabel.
Atau Anda dapat menggunakan Prototipe saja
Dalam JavaScript, pewarisan prototypal terutama merupakan optimasi . Ini memungkinkan beberapa instance untuk berbagi metode prototipe, daripada setiap contoh memiliki metode sendiri.
Kekurangannya adalah satu
this
- satunya hal yang berbeda setiap kali fungsi prototypal dipanggil.Oleh karena itu, setiap bidang pribadi harus dapat diakses melalui
this
, yang berarti mereka akan menjadi publik. Jadi kami hanya tetap pada penamaan konvensi untuk_private
bidang.Jangan repot-repot mencampur Penutupan dengan Prototipe
Saya pikir Anda tidak harus mencampur variabel penutupan dengan metode prototipe. Anda harus menggunakan yang satu atau yang lain.
Ketika Anda menggunakan penutupan untuk mengakses variabel pribadi, metode prototipe tidak dapat mengakses variabel. Jadi, Anda harus mengekspos penutupan itu
this
, yang berarti Anda mengeksposnya secara terbuka. Sangat sedikit yang bisa didapat dengan pendekatan ini.Yang mana yang saya pilih?
Untuk benda yang benar-benar sederhana, cukup gunakan benda biasa dengan penutup.
Jika Anda membutuhkan warisan prototipal - untuk warisan, kinerja, dll - maka tetaplah dengan konvensi penamaan "_private", dan jangan repot-repot dengan penutupan.
Saya tidak mengerti mengapa pengembang JS berusaha keras untuk membuat bidang benar-benar pribadi.
sumber
_private
konvensi penamaan masih merupakan solusi terbaik jika Anda ingin memanfaatkan warisan prototipal.Symbol
, yang merupakan cara terbaik untuk membuat bidang pribadi. Berikut penjelasan yang bagus: curiosity-driven.org/private-properties-in-javascriptSymbol
menutupnya yang mencakup seluruh kelas Anda. Dengan begitu, semua metode prototipe dapat menggunakan Simbol, tetapi tidak pernah diekspos di luar kelas.Object.getOwnPropertySymbols
. Jadi ini hanya privasi karena ketidakjelasan.toString
. Ini tidak berbeda dengan Java atau C # ... anggota pribadi masih dapat diakses melalui refleksi, tetapi biasanya sangat dikaburkan. Yang semuanya memperkuat poin terakhir saya, "Saya tidak mengerti mengapa pengembang JS berusaha keras untuk membuat bidang benar-benar pribadi."Ketika saya membaca ini, itu terdengar seperti tantangan yang sulit jadi saya memutuskan untuk mencari cara. Apa yang saya temukan adalah CRAAAAZY tetapi benar-benar berfungsi.
Pertama, saya mencoba mendefinisikan kelas dalam fungsi langsung sehingga Anda akan memiliki akses ke beberapa properti pribadi dari fungsi itu. Ini berfungsi dan memungkinkan Anda untuk mendapatkan beberapa data pribadi, namun, jika Anda mencoba mengatur data pribadi Anda akan segera menemukan bahwa semua objek akan berbagi nilai yang sama.
Ada banyak kasus di mana ini akan memadai seperti jika Anda ingin memiliki nilai konstan seperti nama acara yang dibagikan di antara instance. Tetapi pada dasarnya, mereka bertindak seperti variabel statis pribadi.
Jika Anda benar-benar membutuhkan akses ke variabel dalam namespace pribadi dari dalam metode Anda yang ditentukan pada prototipe, Anda dapat mencoba pola ini.
Saya suka umpan balik dari siapa pun yang melihat kesalahan dengan cara melakukannya.
sumber
i
telah ditambahkan ke semua instance. Jadi itu tidak sepenuhnya "transparan", dani
masih bisa dirusak.lihat halaman Doug Crockford tentang ini . Anda harus melakukannya secara tidak langsung dengan sesuatu yang dapat mengakses ruang lingkup variabel pribadi.
contoh lain:
gunakan case:
sumber
_set
viaset
? Mengapa tidak menyebutkannya sajaset
?Saya menyarankan itu mungkin akan menjadi ide yang baik untuk menggambarkan "memiliki tugas prototipe dalam konstruktor" sebagai anti-pola Javascript. Pikirkan tentang itu. Itu terlalu berisiko.
Apa yang sebenarnya Anda lakukan di sana pada pembuatan objek kedua (yaitu b) adalah mendefinisikan kembali fungsi prototipe untuk semua objek yang menggunakan prototipe itu. Ini akan secara efektif mengatur ulang nilai untuk objek dalam contoh Anda. Ini akan berfungsi jika Anda ingin variabel yang dibagikan dan jika Anda membuat semua instance objek di depan, tetapi rasanya terlalu berisiko.
Saya menemukan bug di beberapa Javascript yang saya kerjakan baru-baru ini karena anti-pola yang tepat ini. Itu mencoba mengatur drag and drop handler pada objek tertentu yang sedang dibuat tetapi malah melakukannya untuk semua contoh. Tidak baik.
Solusi Doug Crockford adalah yang terbaik.
sumber
@ Kai
Itu tidak akan berhasil. Jika kamu melakukan
kemudian
t2.prototypeHello
akan mengakses bagian pribadi t.@AnglesCrimes
Kode sampel berfungsi dengan baik, tetapi sebenarnya membuat anggota pribadi "statis" dibagikan oleh semua contoh. Ini mungkin bukan solusi yang dicari oleh morgancodes.
Sejauh ini saya belum menemukan cara yang mudah dan bersih untuk melakukan ini tanpa memperkenalkan fungsi pembersihan dan hash pribadi. Fungsi anggota pribadi dapat disimulasikan sampai batas tertentu:
sumber
privateFoo
sepenuhnya pribadi dan dengan demikian tidak terlihat ketika mendapatkannew Foo()
. Hanyabar()
metode publik di sini, yang memiliki akses keprivateFoo
. Anda bisa menggunakan mekanisme yang sama untuk variabel dan objek sederhana, namun Anda harus selalu ingat bahwa ituprivates
sebenarnya statis dan akan dibagikan oleh semua objek yang Anda buat.Iya itu mungkin. Pola desain PPF baru saja menyelesaikan ini.
PPF adalah singkatan dari Private Prototype Functions. PPF dasar menyelesaikan masalah ini:
Untuk yang pertama, cukup:
Sesederhana itu. Sebagai contoh:
...
Baca cerita selengkapnya di sini:
Pola Desain PPF
sumber
Anda benar-benar dapat mencapai ini dengan menggunakan Verifikasi Accessor :
Contoh ini berasal dari posting saya tentang Fungsi Prototip & Data Pribadi dan dijelaskan secara lebih rinci di sana.
sumber
Dalam JavaScript saat ini, saya cukup yakin bahwa ada satu dan hanya satu cara untuk memiliki negara pribadi , dapat diakses dari fungsi prototipe , tanpa menambahkan apa pun untuk publik
this
. Jawabannya adalah dengan menggunakan pola "peta lemah".Untuk meringkasnya:
Person
Kelas memiliki peta lemah tunggal, di mana kuncinya adalah contoh Person, dan nilainya adalah objek polos yang digunakan untuk penyimpanan pribadi.Berikut ini adalah contoh yang berfungsi penuh: (bermain di http://jsfiddle.net/ScottRippey/BLNVr/ )
Seperti saya katakan, ini benar-benar satu-satunya cara untuk mencapai semua 3 bagian.
Namun, ada dua peringatan. Pertama, ini membutuhkan kinerja - setiap kali Anda mengakses data pribadi, ini adalah
O(n)
operasi, di manan
jumlah instance. Jadi Anda tidak akan mau melakukan ini jika Anda memiliki banyak contoh. Kedua, ketika Anda selesai dengan sebuah instance, Anda harus menelepondestroy
; jika tidak, instance dan data tidak akan menjadi sampah yang dikumpulkan, dan Anda akan berakhir dengan kebocoran memori.Dan itulah sebabnya jawaban awal saya, "Anda seharusnya tidak" , adalah sesuatu yang ingin saya pertahankan.
sumber
Ada cara yang lebih sederhana dengan memanfaatkan penggunaan
bind
dancall
metode.Dengan mengatur variabel pribadi ke objek, Anda dapat memanfaatkan cakupan objek itu.
Contoh
Metode ini bukan tanpa kekurangan. Karena konteks cakupan ditimpa secara efektif, Anda tidak memiliki akses di luar
_private
objek. Namun, bukan tidak mungkin untuk tetap memberikan akses ke cakupan objek instance. Anda bisa meneruskan dalam konteks objek (this
) sebagai argumen keduabind
ataucall
masih memiliki akses ke nilai-nilai publik itu dalam fungsi prototipe.Mengakses nilai-nilai publik
sumber
Cobalah!
sumber
caller
, yang merupakan ekstensi bergantung pada implementasi tidak diizinkan dalam mode ketat.Inilah yang saya pikirkan.
masalah utama dengan implementasi ini adalah ia mendefinisikan ulang prototipe pada setiap instanciation.
sumber
Ada cara yang sangat sederhana untuk melakukan ini
Prototipe JavaScript berwarna emas.
sumber
SharedPrivate.prototype
karenathis.constructor.prototype
ini bukan masalah besar untuk mendefinisikan ulang getP dan setP beberapa kali ...Saya terlambat ke pesta, tapi saya pikir saya bisa berkontribusi. Di sini, lihat ini:
Saya menyebutnya pola pengakses metode ini . Ide dasarnya adalah bahwa kami memiliki penutup , kunci di dalam penutupan, dan kami membuat objek pribadi (dalam konstruktor) yang hanya dapat diakses jika Anda memiliki kunci .
Jika Anda tertarik, Anda dapat membaca lebih lanjut tentang ini di artikel saya . Menggunakan metode ini, Anda bisa membuat per objek properti yang tidak dapat diakses di luar penutupan. Oleh karena itu, Anda dapat menggunakannya dalam konstruktor atau prototipe, tetapi tidak di tempat lain. Saya belum pernah melihat metode ini digunakan di mana pun, tetapi saya pikir ini sangat kuat.
sumber
Tidak bisakah Anda menempatkan variabel dalam cakupan yang lebih tinggi?
sumber
Anda juga dapat mencoba menambahkan metode tidak secara langsung pada prototipe, tetapi pada fungsi konstruktor seperti ini:
sumber
Inilah sesuatu yang saya buat ketika mencoba menemukan solusi paling sederhana untuk masalah ini, mungkin itu bisa bermanfaat bagi seseorang. Saya baru mengenal javascript, jadi mungkin ada beberapa masalah dengan kode tersebut.
sumber
Saya menghadapi pertanyaan yang sama persis hari ini dan setelah menguraikan respons kelas satu Scott Rippey, saya datang dengan solusi yang sangat sederhana (IMHO) yang keduanya kompatibel dengan ES5 dan efisien, juga aman untuk nama clash (menggunakan _private tampaknya tidak aman) .
Diuji dengan ringojs dan nodejs. Saya ingin membaca pendapat Anda.
sumber
Bagaimana dengan ini? Menggunakan accessor pribadi. Hanya memungkinkan Anda untuk mendapatkan variabel meskipun tidak mengaturnya, tergantung pada use case.
sumber
Saya punya satu solusi, tetapi saya tidak yakin itu tanpa cacat.
Agar berfungsi, Anda harus menggunakan struktur berikut:
Ini kodenya:
Cara kerjanya adalah menyediakan fungsi instance "this.getPrivateFields" untuk mengakses objek variabel privat "privateFields", tetapi fungsi ini hanya akan mengembalikan objek "privateFields" di dalam penutupan utama yang ditentukan (juga fungsi prototipe menggunakan "this.getPrivateFields "perlu didefinisikan di dalam penutupan ini).
Hash yang dihasilkan selama runtime dan sulit ditebak digunakan sebagai parameter untuk memastikan bahwa meskipun "getPrivateFields" disebut di luar lingkup penutupan tidak akan mengembalikan objek "privateFields".
Kekurangannya adalah kita tidak bisa memperpanjang TestClass dengan lebih banyak fungsi prototipe di luar penutupan.
Berikut ini beberapa kode tes:
EDIT: Menggunakan metode ini, juga dimungkinkan untuk "mendefinisikan" fungsi pribadi.
sumber
Bermain-main dengan ini hari ini dan ini adalah satu-satunya solusi yang bisa kutemukan tanpa menggunakan Simbol. Hal terbaik tentang ini adalah semuanya bisa benar-benar pribadi.
Solusinya didasarkan di sekitar loader modul homegrown yang pada dasarnya menjadi mediator untuk cache penyimpanan pribadi (menggunakan peta yang lemah).
sumber
Saya tahu ini sudah lebih dari 1 dekade sejak ditanya, tetapi saya hanya memikirkan ini untuk kali ke-9 dalam kehidupan programmer saya, dan menemukan solusi yang mungkin saya tidak tahu apakah saya sepenuhnya suka belum . Saya belum pernah melihat metodologi ini didokumentasikan sebelumnya, jadi saya akan menamainya "pola dolar privat / publik" atau pola _ $ / $ .
Konsep ini menggunakan fungsi ClassDefinition yang mengembalikan fungsi Konstruktor yang mengembalikan objek Interface . Satu-satunya metode antarmuka adalah
$
yang menerimaname
argumen untuk memanggil fungsi yang sesuai dalam objek konstruktor, setiap argumen tambahan yang diteruskan setelahname
diteruskan dalam doa.Fungsi helper yang didefinisikan secara global
ClassValues
menyimpan semua bidang dalam suatu objek sesuai kebutuhan. Ini mendefinisikan_$
fungsi untuk mengaksesnyaname
. Ini mengikuti pola get / set pendek jadi jikavalue
dilewatkan, itu akan digunakan sebagai nilai variabel baru.Fungsi yang didefinisikan secara global
Interface
mengambil objek danValues
objek untuk mengembalikan sebuah_interface
dengan satu fungsi tunggal$
yang memeriksaobj
untuk menemukan fungsi yang dinamai parametername
dan memanggilnya denganvalues
sebagai objek scoped . Argumen tambahan yang diteruskan$
akan diteruskan pada pemanggilan fungsi.Dalam sampel di bawah ini,
ClassX
ditugaskan untuk hasilClassDefinition
, yang merupakanConstructor
fungsinya.Constructor
dapat menerima sejumlah argumen.Interface
adalah apa yang didapat kode eksternal setelah memanggil konstruktor.Tidak ada gunanya memiliki fungsi non-prototip
Constructor
, meskipun Anda bisa mendefinisikannya di badan fungsi konstruktor. Semua fungsi dipanggil dengan pola dolar publikthis.$("functionName"[, param1[, param2 ...]])
. Nilai-nilai pribadi diakses dengan pola dolar pribadithis._$("valueName"[, replacingValue]);
. KarenaInterface
tidak memiliki definisi untuk_$
, nilai tidak dapat diakses oleh objek eksternal. Karena masing-masing fungsi tubuh prototipethis
diatur kevalues
objek dalam fungsi$
, Anda akan mendapatkan pengecualian jika Anda memanggil fungsi saudara Konstruktor secara langsung; pola _ $ / $ perlu diikuti dalam badan fungsi yang di-prototip juga. Penggunaan sampel di bawah ini.Dan output konsol.
Pola _ $ / $ memungkinkan privasi penuh nilai di kelas yang sepenuhnya di-prototyped. Saya tidak tahu apakah saya akan pernah menggunakan ini, atau apakah itu memiliki kekurangan, tapi hei, itu adalah teka-teki yang bagus!
sumber
ES6 WeakMaps
Dengan menggunakan pola sederhana berbasis ES6, WeakMaps dimungkinkan untuk mendapatkan variabel anggota pribadi, yang dapat dijangkau dari fungsi prototipe .
Penjelasan lebih rinci tentang pola ini dapat ditemukan di sini
sumber
Anda perlu mengubah 3 hal dalam kode Anda:
var privateField = "hello"
denganthis.privateField = "hello"
.privateField
denganthis.privateField
.privateField
denganthis.privateField
.Kode akhir akan menjadi sebagai berikut:
sumber
this.privateField
tidak akan menjadi bidang pribadi. itu dapat diakses dari luar:t.privateField
Anda dapat menggunakan tugas prototipe dalam definisi konstruktor.
Variabel akan terlihat oleh metode yang ditambahkan prototipe tetapi semua contoh fungsi akan mengakses variabel SHARED yang sama.
Semoga ini bermanfaat.
sumber