Misalkan saya memiliki objek foo
dalam kode JavaScript saya. foo
adalah objek yang kompleks dan dihasilkan di tempat lain. Bagaimana cara mengubah prototipe foo
objek?
Motivasi saya adalah menyetel prototipe yang sesuai ke objek yang diserialkan dari .NET ke literal JavaScript.
Misalkan saya telah menulis kode JavaScript berikut dalam halaman ASP.NET.
var foo = <%=MyData %>;
Misalkan itu MyData
adalah hasil pemanggilan .NET JavaScriptSerializer
pada sebuah Dictionary<string,string>
objek.
Pada saat run-time, ini menjadi sebagai berikut:
var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}];
Seperti yang Anda lihat, foo
menjadi larik objek. Saya ingin dapat memulai foo
dengan prototipe yang sesuai. Saya tidak ingin mengubah Object.prototype
atau Array.prototype
. Bagaimana saya bisa melakukan ini?
javascript
function-prototypes
Sungai Vivian
sumber
sumber
extend
atau Googlegoog.inherit
? Banyak pengembang menyediakan cara untuk membangun warisan sebelum memanggilnew
konstruktor tua — yang sebelum kami diberikanObject.create
dan tidak perlu khawatir tentang menimpaObject.prototype
.Jawaban:
EDIT Februari 2012: jawaban di bawah ini sudah tidak akurat lagi. __proto__ sedang ditambahkan ke ECMAScript 6 sebagai "normatif opsional" yang berarti tidak diharuskan untuk diterapkan tetapi jika demikian, itu harus mengikuti seperangkat aturan yang diberikan. Ini saat ini belum terselesaikan tetapi setidaknya itu akan menjadi bagian resmi dari spesifikasi JavaScript.
Pertanyaan ini jauh lebih rumit daripada yang terlihat di permukaan, dan di luar nilai gaji kebanyakan orang dalam hal pengetahuan tentang internal Javascript.
The
prototype
properti dari objek yang digunakan saat membuat objek anak baru dari objek itu. Mengubahnya tidak mencerminkan objek itu sendiri, melainkan tercermin ketika objek itu digunakan sebagai konstruktor untuk objek lain, dan tidak ada gunanya mengubah prototipe objek yang sudah ada.Objek memiliki properti [[prototipe]] internal yang mengarah ke prototipe saat ini. Cara kerjanya adalah setiap kali sebuah properti pada sebuah objek dipanggil, ia akan mulai pada objek tersebut dan kemudian naik melalui rantai [[prototipe]] sampai ia menemukan kecocokan, atau gagal, setelah prototipe objek root. Ini adalah bagaimana Javascript memungkinkan untuk pembangunan runtime dan modifikasi objek; itu memiliki rencana untuk mencari apa yang dibutuhkannya.
The
__proto__
properti ada di beberapa implementasi (banyak sekarang): setiap pelaksanaan Mozilla, semua yang webkit saya tahu, beberapa orang lain. Properti ini menunjuk ke properti [[prototipe]] internal dan memungkinkan modifikasi pasca-pembuatan pada objek. Semua properti dan fungsi akan langsung beralih agar sesuai dengan prototipe karena pencarian berantai ini.Fitur ini, meskipun distandarisasi sekarang, masih belum menjadi bagian wajib dari JavaScript, dan dalam bahasa yang mendukungnya memiliki kemungkinan besar untuk menjatuhkan kode Anda ke dalam kategori "tidak dioptimalkan". Mesin JS harus melakukan yang terbaik untuk mengklasifikasikan kode, terutama kode "panas" yang sangat sering diakses, dan jika Anda melakukan sesuatu yang menarik seperti memodifikasi
__proto__
, mereka tidak akan mengoptimalkan kode Anda sama sekali.Posting ini https://bugzilla.mozilla.org/show_bug.cgi?id=607863 secara khusus membahas implementasi saat ini
__proto__
dan perbedaan di antara mereka. Setiap implementasi melakukannya secara berbeda, karena ini masalah yang sulit dan tidak terpecahkan. Segala sesuatu di Javascript bisa berubah, kecuali a.) Sintaks b.) Objek host (DOM ada di luar Javascript secara teknis) dan c.)__proto__
. Sisanya sepenuhnya ada di tangan Anda dan setiap pengembang lainnya, jadi Anda dapat melihat mengapa__proto__
menonjol seperti ibu jari yang sakit.Ada satu hal yang
__proto__
memungkinkan hal itu tidak mungkin dilakukan: penunjukan prototipe objek pada waktu proses terpisah dari konstruktornya. Ini adalah kasus penggunaan yang penting dan merupakan salah satu alasan utama__proto__
belum mati. Cukup penting bahwa ini menjadi poin diskusi yang serius dalam perumusan Harmoni, atau segera dikenal sebagai ECMAScript 6. Kemampuan untuk menentukan prototipe suatu objek selama pembuatan akan menjadi bagian dari versi Javascript berikutnya dan ini akan menjadi bel yang menunjukkan__proto__
hari diberi nomor secara resmi.Dalam jangka pendek, Anda dapat menggunakan
__proto__
jika Anda menargetkan browser yang mendukungnya (bukan IE, dan tidak ada IE yang akan mendukungnya). Kemungkinan itu akan berfungsi di webkit dan moz selama 10 tahun ke depan karena ES6 tidak akan selesai hingga 2013.Brendan Eich - re: Pendekatan metode Objek baru di ES5 :
sumber
non-writable, configurable
akan memperbaiki masalah ini dengan memaksa pengguna untuk mengkonfigurasi ulang properti secara eksplisit. Pada akhirnya, praktik buruk dikaitkan dengan penyalahgunaan kapabilitas bahasa, bukan kapabilitas itu sendiri. __Proto__ yang dapat ditulis bukannya tidak pernah terdengar. Ada banyak properti lain yang tidak dapat dihitung dan, meskipun ada bahayanya, ada praktik terbaik juga. Kepala palu tidak boleh dilepas hanya karena dapat digunakan secara tidak benar untuk melukai seseorang.__proto__
diperlukan, karena Object.create hanya akan menghasilkan Objek, bukan fungsi misalnya, atau Simbol, Regex, elemen DOM atau objek host lainnya. Jika Anda ingin objek Anda dapat dipanggil, atau khusus dengan cara lain, tetapi masih mengubah rantai prototipe mereka, Anda terjebak tanpa dapat__proto__
__proto__
dalam JSON". Kekhawatiran Brendan Eich lainnya menggelikan. Rantai prototipe rekursif atau menggunakan prototipe dengan metode yang tidak memadai untuk objek yang diberikan adalah kesalahan programmer, bukan kesalahan bahasa dan seharusnya tidak menjadi faktor, dan selain itu bisa terjadi dengan Object.create serta dengan setel bebas__proto__
.__proto__
.ES6 akhirnya menentukan Object.setPrototypeOf (object, prototype) yang sudah diimplementasikan di Chrome dan Firefox.
sumber
Anda dapat menggunakan
constructor
sebuah instance dari sebuah objek untuk mengubah prototipe sebuah objek di tempat. Saya percaya inilah yang Anda minta untuk dilakukan.Ini berarti jika Anda memiliki
foo
yang merupakan contoh dariFoo
:Anda dapat menambahkan properti
bar
ke semua contohFoo
dengan melakukan hal berikut:Berikut biola yang menunjukkan bukti konsep: http://jsfiddle.net/C2cpw/ . Tidak terlalu yakin bagaimana browser lama akan bekerja dengan pendekatan ini, tapi saya cukup yakin ini akan melakukan pekerjaan dengan cukup baik.
Jika Anda bermaksud untuk mencampur fungsionalitas ke dalam objek, cuplikan ini harus melakukan tugasnya:
sumber
foo.__proto__.bar = 'bar';
Anda bisa melakukannya
foo.__proto__ = FooClass.prototype
, AFAIK yang didukung oleh Firefox, Chrome dan Safari. Perlu diingat bahwa__proto__
properti tersebut tidak standar dan mungkin akan hilang pada suatu saat.Dokumentasi: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto . Lihat juga http://www.mail-archive.com/[email protected]/msg00392.html untuk penjelasan mengapa tidak ada
Object.setPrototypeOf()
dan mengapa__proto__
dihentikan.sumber
Anda dapat menentukan fungsi konstruktor proxy Anda dan kemudian membuat instance baru dan menyalin semua properti dari objek asli ke sana.
Demo langsung: http://jsfiddle.net/6Xq3P/
The
Custom
konstruktor merupakan prototipe baru, ergo, yangCustom.prototype
objek berisi semua properti baru yang ingin Anda gunakan dengan objek asli Anda.Di dalam
Custom
konstruktor, Anda cukup menyalin semua properti dari objek asli ke objek instance baru.Objek instance baru ini berisi semua properti dari objek asli (mereka disalin ke dalamnya di dalam konstruktor), dan juga semua properti baru yang ditentukan di dalamnya
Custom.prototype
(karena objek baru adalahCustom
instance).sumber
Anda tidak dapat mengubah prototipe objek JavaScript yang sudah dibuat instance-nya dengan cara lintas browser. Seperti yang telah disebutkan orang lain, opsi Anda meliputi:
__proto__
propertiKeduanya tidak terlalu bagus, terutama jika Anda harus mengulang secara rekursif melalui objek menjadi objek dalam untuk secara efektif mengubah seluruh elemen prototipe.
Solusi alternatif untuk pertanyaan
Saya akan melihat lebih abstrak pada fungsionalitas yang Anda inginkan.
Pada dasarnya prototipe / metode hanya memungkinkan cara untuk mengelompokkan fungsi berdasarkan suatu objek.
Alih-alih menulis
Anda menulis
Sintaks di atas telah menciptakan istilah OOP karena sintaks object.method (). Beberapa keunggulan utama OOP dibandingkan pemrograman fungsional tradisional meliputi:
obj.replace('needle','replaced')
vs harus mengingat nama sepertistr_replace ( 'foo' , 'bar' , 'subject')
dan lokasi variabel yang berbedastring.trim().split().join()
) adalah fungsi yang berpotensi lebih mudah untuk dimodifikasi dan ditulis kemudian fungsi bersarangjoin(split(trim(string))
Sayangnya di JavaScript (seperti yang ditunjukkan di atas) Anda tidak dapat memodifikasi prototipe yang sudah ada. Idealnya di atas Anda dapat memodifikasi
Object.prototype
hanya untuk Objek yang diberikan di atas, tetapi sayangnya memodifikasiObject.prototype
akan berpotensi merusak skrip (mengakibatkan benturan properti dan menimpa).Tidak ada jalan tengah yang umum digunakan antara 2 gaya pemrograman ini, dan tidak ada cara OOP untuk mengatur fungsi kustom.
UnlimitJS menyediakan jalan tengah yang memungkinkan Anda menentukan metode kustom. Ini menghindari:
Menggunakan kode Anda di atas, saya hanya akan membuat namespace dari fungsi yang ingin Anda panggil terhadap objek.
Berikut ini contohnya:
Anda dapat membaca lebih banyak contoh di sini UnlimitJS . Pada dasarnya saat Anda memanggil
[Unlimit]()
suatu fungsi, ini memungkinkan fungsi tersebut dipanggil sebagai metode pada Objek. Ini seperti jalan tengah antara OOP dan jalan fungsional.sumber
[[prototype]]
Sejauh yang saya tahu , referensi dari objek yang sudah dibuat tidak dapat diubah . Anda dapat mengubah properti prototipe dari fungsi konstruktor asli tetapi, seperti yang telah Anda komentari, konstruktor itu adalahObject
, dan mengubah konstruksi inti JS adalah Hal yang Buruk.Anda dapat membuat objek proxy dari objek yang dibangun yang mengimplementasikan fungsionalitas tambahan yang Anda butuhkan. Anda juga bisa mencocokkan metode dan perilaku tambahan dengan menugaskan langsung ke objek yang dimaksud.
Mungkin Anda bisa mendapatkan apa yang Anda inginkan dengan cara lain, jika Anda bersedia melakukan pendekatan dari sudut yang berbeda: Apa yang perlu Anda lakukan yang melibatkan mengotak-atik prototipe?
sumber
__proto__
propertinya. Ini hanya akan berfungsi di browser yang mendukung__proto__
notasi, yaitu Chrome dan Firefox, dan sudah tidak digunakan lagi . Jadi, singkatnya, Anda dapat mengubah[[prototype]]
suatu objek, tetapi mungkin tidak seharusnya.Jika Anda mengetahui prototipe tersebut, mengapa tidak memasukkannya ke dalam kode?
Jadi, setelah data diserialkan, Anda mendapatkan
sekarang Anda hanya membutuhkan konstruktor yang menggunakan array sebagai argumen.
sumber
Tidak ada cara untuk benar-benar mewarisi dari
Array
atau "sub-kelas" itu.Apa yang dapat Anda lakukan adalah ini ( PERINGATAN: KODE FESTERING DEPAN ):
Ini berfungsi, tetapi akan menyebabkan masalah tertentu bagi siapa pun yang melintasi jalurnya (terlihat seperti sebuah array, tetapi ada yang salah jika Anda mencoba memanipulasinya).
Mengapa Anda tidak menggunakan rute yang waras dan menambahkan metode / properti secara langsung
foo
, atau menggunakan konstruktor dan menyimpan array Anda sebagai properti?sumber
jika Anda ingin membuat prototipe dengan cepat, ini adalah salah satu caranya
sumber
sumber
prototype
statis? Saya tidak yakin apa yang Anda maksud.prototype
adalah properti suatu fungsi, bukan objek.Object.prototype
ada;{}.prototype
tidak.Object.getPrototypeOf(foo)
untuk mendapatkan objek prototipe konstruktor. Anda dapat mengubah properti itu untuk memodifikasi prototipe objek. Ini hanya berfungsi di browser terbaru, tidak bisa memberi tahu Anda yang mana.Object.prototype
karena kemungkinan besar itulah yangObject.getPrototypeOf(foo)
akan kembali. Tidak terlalu berguna.