Saya mengetahui cara membuat getter dan setter untuk properti yang namanya sudah diketahui, dengan melakukan sesuatu seperti ini:
// A trivial example:
function MyObject(val){
this.count = 0;
this.value = val;
}
MyObject.prototype = {
get value(){
return this.count < 2 ? "Go away" : this._value;
},
set value(val){
this._value = val + (++this.count);
}
};
var a = new MyObject('foo');
alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"
Sekarang, pertanyaan saya adalah, apakah mungkin untuk menentukan jenis catch-all getter dan setter seperti ini? Yaitu, buat getter dan setter untuk nama properti apa pun yang belum ditentukan.
Konsep ini mungkin dalam PHP menggunakan metode __get()
dan __set()
sihir (lihat dokumentasi PHP untuk informasi tentang ini), jadi saya benar-benar bertanya apakah ada JavaScript yang setara dengan ini?
Tak perlu dikatakan, saya idealnya menginginkan solusi yang kompatibel lintas-browser.
javascript
metaprogramming
getter-setter
daiscog
sumber
sumber
Jawaban:
Pembaruan 2013 dan 2015 (lihat di bawah untuk jawaban asli dari 2011) :
Ini berubah pada spesifikasi ES2015 (alias "ES6"): JavaScript sekarang memiliki proxy . Proxy memungkinkan Anda membuat objek yang benar-benar proxy untuk (fasad) objek lain. Berikut adalah contoh sederhana yang mengubah nilai properti apa pun yang merupakan string ke semua batas saat pengambilan:
Operasi yang tidak Anda timpa memiliki perilaku default. Di atas, semua yang kami timpa adalah
get
, tetapi ada seluruh daftar operasi yang dapat Anda lakukan.Dalam
get
daftar argumen fungsi pawang:target
adalah objek yang sedang diproksikan (original
, dalam kasus kami).name
adalah (tentu saja) nama properti yang diambil, yang biasanya berupa string tetapi juga bisa menjadi Simbol.receiver
adalah objek yang harus digunakan sepertithis
pada fungsi pengambil jika properti adalah accessor daripada properti data. Dalam kasus normal ini adalah proksi atau sesuatu yang mewarisi darinya, tetapi bisa berupa apa saja karena perangkap mungkin dipicu olehReflect.get
.Ini memungkinkan Anda membuat objek dengan fitur catch-all getter dan setter yang Anda inginkan:
Output di atas adalah:
Perhatikan bagaimana kita mendapatkan pesan "tidak ada" ketika kita mencoba mengambilnya
foo
ketika belum ada, dan lagi ketika kita membuatnya, tetapi tidak setelah itu.Jawaban dari 2011 (lihat pembaruan di atas untuk 2013 dan 2015) :
Tidak, JavaScript tidak memiliki fitur properti tangkap semua. Sintaks accessor yang Anda gunakan tercakup dalam Bagian 11.1.5 dari spesifikasi, dan tidak menawarkan wildcard atau sesuatu seperti itu.
Anda tentu saja dapat mengimplementasikan fungsi untuk melakukannya, tetapi saya rasa Anda mungkin tidak ingin menggunakan
f = obj.prop("foo");
daripadaf = obj.foo;
danobj.prop("foo", value);
bukannyaobj.foo = value;
(yang akan diperlukan untuk fungsi untuk menangani properti yang tidak diketahui).FWIW, fungsi pengambil (saya tidak repot-repot dengan logika setter) akan terlihat seperti ini:
Tetapi sekali lagi, saya tidak bisa membayangkan Anda benar-benar ingin melakukan itu, karena cara itu mengubah cara Anda menggunakan objek.
sumber
Proxy
:Object.defineProperty()
. Saya memasukkan detail dalam jawaban baru saya .Berikut ini bisa menjadi pendekatan asli untuk masalah ini:
Untuk menggunakannya properti harus dilewatkan sebagai string. Jadi inilah contoh cara kerjanya:
Sunting: Pendekatan yang ditingkatkan, lebih berorientasi objek berdasarkan apa yang saya usulkan adalah sebagai berikut:
Anda dapat melihatnya bekerja di sini .
sumber
Kata pengantar:
Jawaban TJ Crowder menyebutkan a
Proxy
, yang akan dibutuhkan untuk semua pengambil / penyetel untuk properti yang tidak ada, seperti yang diminta OP. Bergantung pada perilaku apa yang sebenarnya diinginkan dengan getter / setters yang dinamis,Proxy
mungkin saja sebenarnya tidak diperlukan; atau, berpotensi, Anda mungkin ingin menggunakan kombinasi aProxy
dengan apa yang akan saya tunjukkan di bawah ini.(PS Saya telah bereksperimen dengan
Proxy
tuntas di Firefox di Linux baru-baru ini dan telah menemukan itu sangat mampu, tetapi juga agak membingungkan / sulit untuk dikerjakan dan diperbaiki. Lebih penting lagi, saya juga menemukan itu sangat lambat (setidaknya di kaitannya dengan seberapa optimalnya JavaScript saat ini) - Saya berbicara lebih lambat di ranah deca-kelipatan.)Untuk menerapkan pengambil dan setter yang dibuat secara dinamis secara khusus, Anda dapat menggunakan
Object.defineProperty()
atauObject.defineProperties()
. Ini juga cukup cepat.Intinya adalah Anda dapat mendefinisikan pengambil dan / atau penyetel pada objek seperti:
Beberapa hal yang perlu diperhatikan di sini:
value
properti di deskriptor properti ( tidak ditampilkan di atas) secara bersamaan denganget
dan / atauset
; dari dokumen:val
properti di luar dariObject.defineProperty()
descriptor call / properti. Ini adalah perilaku standar.writable
ketrue
deskriptor properti jika Anda menggunakanget
atauset
.configurable
danenumerable
, bagaimanapun, tergantung pada apa yang Anda cari; dari dokumen:Pada catatan ini, ini mungkin juga menarik:
Object.getOwnPropertyNames(obj)
: dapatkan semua properti dari objek, bahkan yang tidak dapat dihitung (AFAIK, ini adalah satu - satunya cara untuk melakukannya!).Object.getOwnPropertyDescriptor(obj, prop)
: mendapat deskriptor properti dari suatu objek, objek yang diteruskan keObject.defineProperty()
atas.obj.propertyIsEnumerable(prop);
: untuk properti individual pada instance objek tertentu, panggil fungsi ini pada instance objek untuk menentukan apakah properti tertentu dapat dihitung atau tidak.sumber
__get
dan__set
.defineProperty
tidak menangani kasus itu. Dari pertanyaan: "Yaitu, buat getter dan setter untuk nama properti apa pun yang belum ditentukan." (Penekanan mereka).defineProperty
mendefinisikan properti terlebih dahulu. Satu-satunya cara untuk melakukan apa yang diminta OP adalah proxy.obj.whateverProperty
sehingga perpustakaan dapat mencegatnya dengan pembuat getar generik dan diberi nama properti pengguna mencoba mengakses. Oleh karena itu persyaratan untuk 'menangkap semua getter dan setter'.ini bekerja untuk saya
sumber
Function()
seperti itu seperti menggunakaneval
. Langsung saja fungsinya sebagai parameterdefineProperty
. Atau, jika karena alasan tertentu Anda bersikeras untuk membuatget
danset
, kemudian menggunakan fungsi tingkat tinggi yang membuat fungsi dan mengembalikannya, sepertivar get = (function(propName) { return function() { return this[propName]; };})('value');